xref: /freebsd/sys/rpc/clnt_dg.c (revision dfdcada31e7924c832024404c6a09a2db04e397e)
1dfdcada3SDoug Rabson /*	$NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $	*/
2dfdcada3SDoug Rabson 
3dfdcada3SDoug Rabson /*
4dfdcada3SDoug Rabson  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5dfdcada3SDoug Rabson  * unrestricted use provided that this legend is included on all tape
6dfdcada3SDoug Rabson  * media and as a part of the software program in whole or part.  Users
7dfdcada3SDoug Rabson  * may copy or modify Sun RPC without charge, but are not authorized
8dfdcada3SDoug Rabson  * to license or distribute it to anyone else except as part of a product or
9dfdcada3SDoug Rabson  * program developed by the user.
10dfdcada3SDoug Rabson  *
11dfdcada3SDoug Rabson  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12dfdcada3SDoug Rabson  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13dfdcada3SDoug Rabson  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14dfdcada3SDoug Rabson  *
15dfdcada3SDoug Rabson  * Sun RPC is provided with no support and without any obligation on the
16dfdcada3SDoug Rabson  * part of Sun Microsystems, Inc. to assist in its use, correction,
17dfdcada3SDoug Rabson  * modification or enhancement.
18dfdcada3SDoug Rabson  *
19dfdcada3SDoug Rabson  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20dfdcada3SDoug Rabson  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21dfdcada3SDoug Rabson  * OR ANY PART THEREOF.
22dfdcada3SDoug Rabson  *
23dfdcada3SDoug Rabson  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24dfdcada3SDoug Rabson  * or profits or other special, indirect and consequential damages, even if
25dfdcada3SDoug Rabson  * Sun has been advised of the possibility of such damages.
26dfdcada3SDoug Rabson  *
27dfdcada3SDoug Rabson  * Sun Microsystems, Inc.
28dfdcada3SDoug Rabson  * 2550 Garcia Avenue
29dfdcada3SDoug Rabson  * Mountain View, California  94043
30dfdcada3SDoug Rabson  */
31dfdcada3SDoug Rabson /*
32dfdcada3SDoug Rabson  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33dfdcada3SDoug Rabson  */
34dfdcada3SDoug Rabson 
35dfdcada3SDoug Rabson #if defined(LIBC_SCCS) && !defined(lint)
36dfdcada3SDoug Rabson #ident	"@(#)clnt_dg.c	1.23	94/04/22 SMI"
37dfdcada3SDoug Rabson static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
38dfdcada3SDoug Rabson #endif
39dfdcada3SDoug Rabson #include <sys/cdefs.h>
40dfdcada3SDoug Rabson __FBSDID("$FreeBSD$");
41dfdcada3SDoug Rabson 
42dfdcada3SDoug Rabson /*
43dfdcada3SDoug Rabson  * Implements a connectionless client side RPC.
44dfdcada3SDoug Rabson  */
45dfdcada3SDoug Rabson 
46dfdcada3SDoug Rabson #include <sys/param.h>
47dfdcada3SDoug Rabson #include <sys/systm.h>
48dfdcada3SDoug Rabson #include <sys/lock.h>
49dfdcada3SDoug Rabson #include <sys/malloc.h>
50dfdcada3SDoug Rabson #include <sys/mbuf.h>
51dfdcada3SDoug Rabson #include <sys/mutex.h>
52dfdcada3SDoug Rabson #include <sys/pcpu.h>
53dfdcada3SDoug Rabson #include <sys/proc.h>
54dfdcada3SDoug Rabson #include <sys/socket.h>
55dfdcada3SDoug Rabson #include <sys/socketvar.h>
56dfdcada3SDoug Rabson #include <sys/time.h>
57dfdcada3SDoug Rabson #include <sys/uio.h>
58dfdcada3SDoug Rabson 
59dfdcada3SDoug Rabson #include <rpc/rpc.h>
60dfdcada3SDoug Rabson #include "rpc_com.h"
61dfdcada3SDoug Rabson 
62dfdcada3SDoug Rabson 
63dfdcada3SDoug Rabson #ifdef _FREEFALL_CONFIG
64dfdcada3SDoug Rabson /*
65dfdcada3SDoug Rabson  * Disable RPC exponential back-off for FreeBSD.org systems.
66dfdcada3SDoug Rabson  */
67dfdcada3SDoug Rabson #define	RPC_MAX_BACKOFF		1 /* second */
68dfdcada3SDoug Rabson #else
69dfdcada3SDoug Rabson #define	RPC_MAX_BACKOFF		30 /* seconds */
70dfdcada3SDoug Rabson #endif
71dfdcada3SDoug Rabson 
72dfdcada3SDoug Rabson static bool_t time_not_ok(struct timeval *);
73dfdcada3SDoug Rabson static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
74dfdcada3SDoug Rabson 	    xdrproc_t, void *, struct timeval);
75dfdcada3SDoug Rabson static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
76dfdcada3SDoug Rabson static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
77dfdcada3SDoug Rabson static void clnt_dg_abort(CLIENT *);
78dfdcada3SDoug Rabson static bool_t clnt_dg_control(CLIENT *, u_int, void *);
79dfdcada3SDoug Rabson static void clnt_dg_destroy(CLIENT *);
80dfdcada3SDoug Rabson static void clnt_dg_soupcall(struct socket *so, void *arg, int waitflag);
81dfdcada3SDoug Rabson 
82dfdcada3SDoug Rabson static struct clnt_ops clnt_dg_ops = {
83dfdcada3SDoug Rabson 	.cl_call =	clnt_dg_call,
84dfdcada3SDoug Rabson 	.cl_abort =	clnt_dg_abort,
85dfdcada3SDoug Rabson 	.cl_geterr =	clnt_dg_geterr,
86dfdcada3SDoug Rabson 	.cl_freeres =	clnt_dg_freeres,
87dfdcada3SDoug Rabson 	.cl_destroy =	clnt_dg_destroy,
88dfdcada3SDoug Rabson 	.cl_control =	clnt_dg_control
89dfdcada3SDoug Rabson };
90dfdcada3SDoug Rabson 
91dfdcada3SDoug Rabson static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
92dfdcada3SDoug Rabson 
93dfdcada3SDoug Rabson /*
94dfdcada3SDoug Rabson  * A pending RPC request which awaits a reply.
95dfdcada3SDoug Rabson  */
96dfdcada3SDoug Rabson struct cu_request {
97dfdcada3SDoug Rabson 	TAILQ_ENTRY(cu_request) cr_link;
98dfdcada3SDoug Rabson 	uint32_t		cr_xid;		/* XID of request */
99dfdcada3SDoug Rabson 	struct mbuf		*cr_mrep;	/* reply received by upcall */
100dfdcada3SDoug Rabson 	int			cr_error;	/* any error from upcall */
101dfdcada3SDoug Rabson };
102dfdcada3SDoug Rabson 
103dfdcada3SDoug Rabson TAILQ_HEAD(cu_request_list, cu_request);
104dfdcada3SDoug Rabson 
105dfdcada3SDoug Rabson #define MCALL_MSG_SIZE 24
106dfdcada3SDoug Rabson 
107dfdcada3SDoug Rabson /*
108dfdcada3SDoug Rabson  * This structure is pointed to by the socket's so_upcallarg
109dfdcada3SDoug Rabson  * member. It is separate from the client private data to facilitate
110dfdcada3SDoug Rabson  * multiple clients sharing the same socket. The cs_lock mutex is used
111dfdcada3SDoug Rabson  * to protect all fields of this structure, the socket's receive
112dfdcada3SDoug Rabson  * buffer SOCKBUF_LOCK is used to ensure that exactly one of these
113dfdcada3SDoug Rabson  * structures is installed on the socket.
114dfdcada3SDoug Rabson  */
115dfdcada3SDoug Rabson struct cu_socket {
116dfdcada3SDoug Rabson 	struct mtx		cs_lock;
117dfdcada3SDoug Rabson 	int			cs_refs;	/* Count of clients */
118dfdcada3SDoug Rabson 	struct cu_request_list	cs_pending;	/* Requests awaiting replies */
119dfdcada3SDoug Rabson 
120dfdcada3SDoug Rabson };
121dfdcada3SDoug Rabson 
122dfdcada3SDoug Rabson /*
123dfdcada3SDoug Rabson  * Private data kept per client handle
124dfdcada3SDoug Rabson  */
125dfdcada3SDoug Rabson struct cu_data {
126dfdcada3SDoug Rabson 	struct socket		*cu_socket;	/* connection socket */
127dfdcada3SDoug Rabson 	bool_t			cu_closeit;	/* opened by library */
128dfdcada3SDoug Rabson 	struct sockaddr_storage	cu_raddr;	/* remote address */
129dfdcada3SDoug Rabson 	int			cu_rlen;
130dfdcada3SDoug Rabson 	struct timeval		cu_wait;	/* retransmit interval */
131dfdcada3SDoug Rabson 	struct timeval		cu_total;	/* total time for the call */
132dfdcada3SDoug Rabson 	struct rpc_err		cu_error;
133dfdcada3SDoug Rabson 	uint32_t		cu_xid;
134dfdcada3SDoug Rabson 	char			cu_mcallc[MCALL_MSG_SIZE]; /* marshalled callmsg */
135dfdcada3SDoug Rabson 	size_t			cu_mcalllen;
136dfdcada3SDoug Rabson 	size_t			cu_sendsz;	/* send size */
137dfdcada3SDoug Rabson 	size_t			cu_recvsz;	/* recv size */
138dfdcada3SDoug Rabson 	int			cu_async;
139dfdcada3SDoug Rabson 	int			cu_connect;	/* Use connect(). */
140dfdcada3SDoug Rabson 	int			cu_connected;	/* Have done connect(). */
141dfdcada3SDoug Rabson 	const char		*cu_waitchan;
142dfdcada3SDoug Rabson 	int			cu_waitflag;
143dfdcada3SDoug Rabson };
144dfdcada3SDoug Rabson 
145dfdcada3SDoug Rabson /*
146dfdcada3SDoug Rabson  * Connection less client creation returns with client handle parameters.
147dfdcada3SDoug Rabson  * Default options are set, which the user can change using clnt_control().
148dfdcada3SDoug Rabson  * fd should be open and bound.
149dfdcada3SDoug Rabson  * NB: The rpch->cl_auth is initialized to null authentication.
150dfdcada3SDoug Rabson  * 	Caller may wish to set this something more useful.
151dfdcada3SDoug Rabson  *
152dfdcada3SDoug Rabson  * sendsz and recvsz are the maximum allowable packet sizes that can be
153dfdcada3SDoug Rabson  * sent and received. Normally they are the same, but they can be
154dfdcada3SDoug Rabson  * changed to improve the program efficiency and buffer allocation.
155dfdcada3SDoug Rabson  * If they are 0, use the transport default.
156dfdcada3SDoug Rabson  *
157dfdcada3SDoug Rabson  * If svcaddr is NULL, returns NULL.
158dfdcada3SDoug Rabson  */
159dfdcada3SDoug Rabson CLIENT *
160dfdcada3SDoug Rabson clnt_dg_create(
161dfdcada3SDoug Rabson 	struct socket *so,
162dfdcada3SDoug Rabson 	struct sockaddr *svcaddr,	/* servers address */
163dfdcada3SDoug Rabson 	rpcprog_t program,		/* program number */
164dfdcada3SDoug Rabson 	rpcvers_t version,		/* version number */
165dfdcada3SDoug Rabson 	size_t sendsz,			/* buffer recv size */
166dfdcada3SDoug Rabson 	size_t recvsz)			/* buffer send size */
167dfdcada3SDoug Rabson {
168dfdcada3SDoug Rabson 	CLIENT *cl = NULL;		/* client handle */
169dfdcada3SDoug Rabson 	struct cu_data *cu = NULL;	/* private data */
170dfdcada3SDoug Rabson 	struct cu_socket *cs = NULL;
171dfdcada3SDoug Rabson 	struct timeval now;
172dfdcada3SDoug Rabson 	struct rpc_msg call_msg;
173dfdcada3SDoug Rabson 	struct __rpc_sockinfo si;
174dfdcada3SDoug Rabson 	XDR xdrs;
175dfdcada3SDoug Rabson 
176dfdcada3SDoug Rabson 	if (svcaddr == NULL) {
177dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
178dfdcada3SDoug Rabson 		return (NULL);
179dfdcada3SDoug Rabson 	}
180dfdcada3SDoug Rabson 
181dfdcada3SDoug Rabson 	if (!__rpc_socket2sockinfo(so, &si)) {
182dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_TLIERROR;
183dfdcada3SDoug Rabson 		rpc_createerr.cf_error.re_errno = 0;
184dfdcada3SDoug Rabson 		return (NULL);
185dfdcada3SDoug Rabson 	}
186dfdcada3SDoug Rabson 
187dfdcada3SDoug Rabson 	/*
188dfdcada3SDoug Rabson 	 * Find the receive and the send size
189dfdcada3SDoug Rabson 	 */
190dfdcada3SDoug Rabson 	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
191dfdcada3SDoug Rabson 	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
192dfdcada3SDoug Rabson 	if ((sendsz == 0) || (recvsz == 0)) {
193dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
194dfdcada3SDoug Rabson 		rpc_createerr.cf_error.re_errno = 0;
195dfdcada3SDoug Rabson 		return (NULL);
196dfdcada3SDoug Rabson 	}
197dfdcada3SDoug Rabson 
198dfdcada3SDoug Rabson 	cl = mem_alloc(sizeof (CLIENT));
199dfdcada3SDoug Rabson 
200dfdcada3SDoug Rabson 	/*
201dfdcada3SDoug Rabson 	 * Should be multiple of 4 for XDR.
202dfdcada3SDoug Rabson 	 */
203dfdcada3SDoug Rabson 	sendsz = ((sendsz + 3) / 4) * 4;
204dfdcada3SDoug Rabson 	recvsz = ((recvsz + 3) / 4) * 4;
205dfdcada3SDoug Rabson 	cu = mem_alloc(sizeof (*cu));
206dfdcada3SDoug Rabson 	(void) memcpy(&cu->cu_raddr, svcaddr, (size_t)svcaddr->sa_len);
207dfdcada3SDoug Rabson 	cu->cu_rlen = svcaddr->sa_len;
208dfdcada3SDoug Rabson 	/* Other values can also be set through clnt_control() */
209dfdcada3SDoug Rabson 	cu->cu_wait.tv_sec = 15;	/* heuristically chosen */
210dfdcada3SDoug Rabson 	cu->cu_wait.tv_usec = 0;
211dfdcada3SDoug Rabson 	cu->cu_total.tv_sec = -1;
212dfdcada3SDoug Rabson 	cu->cu_total.tv_usec = -1;
213dfdcada3SDoug Rabson 	cu->cu_sendsz = sendsz;
214dfdcada3SDoug Rabson 	cu->cu_recvsz = recvsz;
215dfdcada3SDoug Rabson 	cu->cu_async = FALSE;
216dfdcada3SDoug Rabson 	cu->cu_connect = FALSE;
217dfdcada3SDoug Rabson 	cu->cu_connected = FALSE;
218dfdcada3SDoug Rabson 	cu->cu_waitchan = "rpcrecv";
219dfdcada3SDoug Rabson 	cu->cu_waitflag = 0;
220dfdcada3SDoug Rabson 	(void) getmicrotime(&now);
221dfdcada3SDoug Rabson 	cu->cu_xid = __RPC_GETXID(&now);
222dfdcada3SDoug Rabson 	call_msg.rm_xid = cu->cu_xid;
223dfdcada3SDoug Rabson 	call_msg.rm_call.cb_prog = program;
224dfdcada3SDoug Rabson 	call_msg.rm_call.cb_vers = version;
225dfdcada3SDoug Rabson 	xdrmem_create(&xdrs, cu->cu_mcallc, MCALL_MSG_SIZE, XDR_ENCODE);
226dfdcada3SDoug Rabson 	if (! xdr_callhdr(&xdrs, &call_msg)) {
227dfdcada3SDoug Rabson 		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
228dfdcada3SDoug Rabson 		rpc_createerr.cf_error.re_errno = 0;
229dfdcada3SDoug Rabson 		goto err2;
230dfdcada3SDoug Rabson 	}
231dfdcada3SDoug Rabson 	cu->cu_mcalllen = XDR_GETPOS(&xdrs);;
232dfdcada3SDoug Rabson 
233dfdcada3SDoug Rabson 	/*
234dfdcada3SDoug Rabson 	 * By default, closeit is always FALSE. It is users responsibility
235dfdcada3SDoug Rabson 	 * to do a close on it, else the user may use clnt_control
236dfdcada3SDoug Rabson 	 * to let clnt_destroy do it for him/her.
237dfdcada3SDoug Rabson 	 */
238dfdcada3SDoug Rabson 	cu->cu_closeit = FALSE;
239dfdcada3SDoug Rabson 	cu->cu_socket = so;
240dfdcada3SDoug Rabson 
241dfdcada3SDoug Rabson 	SOCKBUF_LOCK(&so->so_rcv);
242dfdcada3SDoug Rabson recheck_socket:
243dfdcada3SDoug Rabson 	if (so->so_upcall) {
244dfdcada3SDoug Rabson 		if (so->so_upcall != clnt_dg_soupcall) {
245dfdcada3SDoug Rabson 			SOCKBUF_UNLOCK(&so->so_rcv);
246dfdcada3SDoug Rabson 			printf("clnt_dg_create(): socket already has an incompatible upcall\n");
247dfdcada3SDoug Rabson 			goto err2;
248dfdcada3SDoug Rabson 		}
249dfdcada3SDoug Rabson 		cs = (struct cu_socket *) so->so_upcallarg;
250dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
251dfdcada3SDoug Rabson 		cs->cs_refs++;
252dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
253dfdcada3SDoug Rabson 	} else {
254dfdcada3SDoug Rabson 		/*
255dfdcada3SDoug Rabson 		 * We are the first on this socket - allocate the
256dfdcada3SDoug Rabson 		 * structure and install it in the socket.
257dfdcada3SDoug Rabson 		 */
258dfdcada3SDoug Rabson 		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
259dfdcada3SDoug Rabson 		cs = mem_alloc(sizeof(*cs));
260dfdcada3SDoug Rabson 		SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
261dfdcada3SDoug Rabson 		if (so->so_upcall) {
262dfdcada3SDoug Rabson 			/*
263dfdcada3SDoug Rabson 			 * We have lost a race with some other client.
264dfdcada3SDoug Rabson 			 */
265dfdcada3SDoug Rabson 			mem_free(cs, sizeof(*cs));
266dfdcada3SDoug Rabson 			goto recheck_socket;
267dfdcada3SDoug Rabson 		}
268dfdcada3SDoug Rabson 		mtx_init(&cs->cs_lock, "cs->cs_lock", NULL, MTX_DEF);
269dfdcada3SDoug Rabson 		cs->cs_refs = 1;
270dfdcada3SDoug Rabson 		TAILQ_INIT(&cs->cs_pending);
271dfdcada3SDoug Rabson 		so->so_upcallarg = cs;
272dfdcada3SDoug Rabson 		so->so_upcall = clnt_dg_soupcall;
273dfdcada3SDoug Rabson 		so->so_rcv.sb_flags |= SB_UPCALL;
274dfdcada3SDoug Rabson 	}
275dfdcada3SDoug Rabson 	SOCKBUF_UNLOCK(&so->so_rcv);
276dfdcada3SDoug Rabson 
277dfdcada3SDoug Rabson 	cl->cl_ops = &clnt_dg_ops;
278dfdcada3SDoug Rabson 	cl->cl_private = (caddr_t)(void *)cu;
279dfdcada3SDoug Rabson 	cl->cl_auth = authnone_create();
280dfdcada3SDoug Rabson 	cl->cl_tp = NULL;
281dfdcada3SDoug Rabson 	cl->cl_netid = NULL;
282dfdcada3SDoug Rabson 	return (cl);
283dfdcada3SDoug Rabson err2:
284dfdcada3SDoug Rabson 	if (cl) {
285dfdcada3SDoug Rabson 		mem_free(cl, sizeof (CLIENT));
286dfdcada3SDoug Rabson 		if (cu)
287dfdcada3SDoug Rabson 			mem_free(cu, sizeof (*cu));
288dfdcada3SDoug Rabson 	}
289dfdcada3SDoug Rabson 	return (NULL);
290dfdcada3SDoug Rabson }
291dfdcada3SDoug Rabson 
292dfdcada3SDoug Rabson static enum clnt_stat
293dfdcada3SDoug Rabson clnt_dg_call(
294dfdcada3SDoug Rabson 	CLIENT	*cl,			/* client handle */
295dfdcada3SDoug Rabson 	rpcproc_t	proc,		/* procedure number */
296dfdcada3SDoug Rabson 	xdrproc_t	xargs,		/* xdr routine for args */
297dfdcada3SDoug Rabson 	void		*argsp,		/* pointer to args */
298dfdcada3SDoug Rabson 	xdrproc_t	xresults,	/* xdr routine for results */
299dfdcada3SDoug Rabson 	void		*resultsp,	/* pointer to results */
300dfdcada3SDoug Rabson 	struct timeval	utimeout)	/* seconds to wait before giving up */
301dfdcada3SDoug Rabson {
302dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
303dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
304dfdcada3SDoug Rabson 	XDR xdrs;
305dfdcada3SDoug Rabson 	struct rpc_msg reply_msg;
306dfdcada3SDoug Rabson 	bool_t ok;
307dfdcada3SDoug Rabson 	int nrefreshes = 2;		/* number of times to refresh cred */
308dfdcada3SDoug Rabson 	struct timeval timeout;
309dfdcada3SDoug Rabson 	struct timeval retransmit_time;
310dfdcada3SDoug Rabson 	struct timeval next_sendtime, starttime, time_waited, tv;
311dfdcada3SDoug Rabson 	struct sockaddr *sa;
312dfdcada3SDoug Rabson 	socklen_t salen;
313dfdcada3SDoug Rabson 	uint32_t xid;
314dfdcada3SDoug Rabson 	struct mbuf *mreq = NULL;
315dfdcada3SDoug Rabson 	struct cu_request cr;
316dfdcada3SDoug Rabson 	int error;
317dfdcada3SDoug Rabson 
318dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
319dfdcada3SDoug Rabson 
320dfdcada3SDoug Rabson 	cr.cr_mrep = NULL;
321dfdcada3SDoug Rabson 	cr.cr_error = 0;
322dfdcada3SDoug Rabson 
323dfdcada3SDoug Rabson 	if (cu->cu_total.tv_usec == -1) {
324dfdcada3SDoug Rabson 		timeout = utimeout;	/* use supplied timeout */
325dfdcada3SDoug Rabson 	} else {
326dfdcada3SDoug Rabson 		timeout = cu->cu_total;	/* use default timeout */
327dfdcada3SDoug Rabson 	}
328dfdcada3SDoug Rabson 
329dfdcada3SDoug Rabson 	if (cu->cu_connect && !cu->cu_connected) {
330dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
331dfdcada3SDoug Rabson 		error = soconnect(cu->cu_socket,
332dfdcada3SDoug Rabson 		    (struct sockaddr *)&cu->cu_raddr, curthread);
333dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
334dfdcada3SDoug Rabson 		if (error) {
335dfdcada3SDoug Rabson 			cu->cu_error.re_errno = error;
336dfdcada3SDoug Rabson 			cu->cu_error.re_status = RPC_CANTSEND;
337dfdcada3SDoug Rabson 			goto out;
338dfdcada3SDoug Rabson 		}
339dfdcada3SDoug Rabson 		cu->cu_connected = 1;
340dfdcada3SDoug Rabson 	}
341dfdcada3SDoug Rabson 	if (cu->cu_connected) {
342dfdcada3SDoug Rabson 		sa = NULL;
343dfdcada3SDoug Rabson 		salen = 0;
344dfdcada3SDoug Rabson 	} else {
345dfdcada3SDoug Rabson 		sa = (struct sockaddr *)&cu->cu_raddr;
346dfdcada3SDoug Rabson 		salen = cu->cu_rlen;
347dfdcada3SDoug Rabson 	}
348dfdcada3SDoug Rabson 	time_waited.tv_sec = 0;
349dfdcada3SDoug Rabson 	time_waited.tv_usec = 0;
350dfdcada3SDoug Rabson 	retransmit_time = next_sendtime = cu->cu_wait;
351dfdcada3SDoug Rabson 
352dfdcada3SDoug Rabson 	getmicrotime(&starttime);
353dfdcada3SDoug Rabson 
354dfdcada3SDoug Rabson call_again:
355dfdcada3SDoug Rabson 	mtx_assert(&cs->cs_lock, MA_OWNED);
356dfdcada3SDoug Rabson 
357dfdcada3SDoug Rabson 	cu->cu_xid++;
358dfdcada3SDoug Rabson 	xid = cu->cu_xid;
359dfdcada3SDoug Rabson 
360dfdcada3SDoug Rabson send_again:
361dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
362dfdcada3SDoug Rabson 
363dfdcada3SDoug Rabson 	MGETHDR(mreq, M_WAIT, MT_DATA);
364dfdcada3SDoug Rabson 	MCLGET(mreq, M_WAIT);
365dfdcada3SDoug Rabson 	mreq->m_len = 0;
366dfdcada3SDoug Rabson 	m_append(mreq, cu->cu_mcalllen, cu->cu_mcallc);
367dfdcada3SDoug Rabson 
368dfdcada3SDoug Rabson 	/*
369dfdcada3SDoug Rabson 	 * The XID is the first thing in the request.
370dfdcada3SDoug Rabson 	 */
371dfdcada3SDoug Rabson 	*mtod(mreq, uint32_t *) = htonl(xid);
372dfdcada3SDoug Rabson 
373dfdcada3SDoug Rabson 	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
374dfdcada3SDoug Rabson 
375dfdcada3SDoug Rabson 	if (cu->cu_async == TRUE && xargs == NULL)
376dfdcada3SDoug Rabson 		goto get_reply;
377dfdcada3SDoug Rabson 
378dfdcada3SDoug Rabson 	if ((! XDR_PUTINT32(&xdrs, &proc)) ||
379dfdcada3SDoug Rabson 	    (! AUTH_MARSHALL(cl->cl_auth, &xdrs)) ||
380dfdcada3SDoug Rabson 	    (! (*xargs)(&xdrs, argsp))) {
381dfdcada3SDoug Rabson 		cu->cu_error.re_status = RPC_CANTENCODEARGS;
382dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
383dfdcada3SDoug Rabson 		goto out;
384dfdcada3SDoug Rabson 	}
385dfdcada3SDoug Rabson 	m_fixhdr(mreq);
386dfdcada3SDoug Rabson 
387dfdcada3SDoug Rabson 	cr.cr_xid = xid;
388dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
389dfdcada3SDoug Rabson 	TAILQ_INSERT_TAIL(&cs->cs_pending, &cr, cr_link);
390dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
391dfdcada3SDoug Rabson 
392dfdcada3SDoug Rabson 	/*
393dfdcada3SDoug Rabson 	 * sosend consumes mreq.
394dfdcada3SDoug Rabson 	 */
395dfdcada3SDoug Rabson 	error = sosend(cu->cu_socket, sa, NULL, mreq, NULL, 0, curthread);
396dfdcada3SDoug Rabson 	mreq = NULL;
397dfdcada3SDoug Rabson 
398dfdcada3SDoug Rabson 	/*
399dfdcada3SDoug Rabson 	 * sub-optimal code appears here because we have
400dfdcada3SDoug Rabson 	 * some clock time to spare while the packets are in flight.
401dfdcada3SDoug Rabson 	 * (We assume that this is actually only executed once.)
402dfdcada3SDoug Rabson 	 */
403dfdcada3SDoug Rabson 	reply_msg.acpted_rply.ar_verf = _null_auth;
404dfdcada3SDoug Rabson 	reply_msg.acpted_rply.ar_results.where = resultsp;
405dfdcada3SDoug Rabson 	reply_msg.acpted_rply.ar_results.proc = xresults;
406dfdcada3SDoug Rabson 
407dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
408dfdcada3SDoug Rabson 	if (error) {
409dfdcada3SDoug Rabson 		TAILQ_REMOVE(&cs->cs_pending, &cr, cr_link);
410dfdcada3SDoug Rabson 
411dfdcada3SDoug Rabson 		cu->cu_error.re_errno = error;
412dfdcada3SDoug Rabson 		cu->cu_error.re_status = RPC_CANTSEND;
413dfdcada3SDoug Rabson 		goto out;
414dfdcada3SDoug Rabson 	}
415dfdcada3SDoug Rabson 
416dfdcada3SDoug Rabson 	/*
417dfdcada3SDoug Rabson 	 * Check to see if we got an upcall while waiting for the
418dfdcada3SDoug Rabson 	 * lock. In both these cases, the request has been removed
419dfdcada3SDoug Rabson 	 * from cs->cs_pending.
420dfdcada3SDoug Rabson 	 */
421dfdcada3SDoug Rabson 	if (cr.cr_error) {
422dfdcada3SDoug Rabson 		cu->cu_error.re_errno = cr.cr_error;
423dfdcada3SDoug Rabson 		cu->cu_error.re_status = RPC_CANTRECV;
424dfdcada3SDoug Rabson 		goto out;
425dfdcada3SDoug Rabson 	}
426dfdcada3SDoug Rabson 	if (cr.cr_mrep) {
427dfdcada3SDoug Rabson 		goto got_reply;
428dfdcada3SDoug Rabson 	}
429dfdcada3SDoug Rabson 
430dfdcada3SDoug Rabson 	/*
431dfdcada3SDoug Rabson 	 * Hack to provide rpc-based message passing
432dfdcada3SDoug Rabson 	 */
433dfdcada3SDoug Rabson 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
434dfdcada3SDoug Rabson 		if (cr.cr_xid)
435dfdcada3SDoug Rabson 			TAILQ_REMOVE(&cs->cs_pending, &cr, cr_link);
436dfdcada3SDoug Rabson 		cu->cu_error.re_status = RPC_TIMEDOUT;
437dfdcada3SDoug Rabson 		goto out;
438dfdcada3SDoug Rabson 	}
439dfdcada3SDoug Rabson 
440dfdcada3SDoug Rabson get_reply:
441dfdcada3SDoug Rabson 	for (;;) {
442dfdcada3SDoug Rabson 		/* Decide how long to wait. */
443dfdcada3SDoug Rabson 		if (timevalcmp(&next_sendtime, &timeout, <)) {
444dfdcada3SDoug Rabson 			tv = next_sendtime;
445dfdcada3SDoug Rabson 		} else {
446dfdcada3SDoug Rabson 			tv = timeout;
447dfdcada3SDoug Rabson 		}
448dfdcada3SDoug Rabson 		timevalsub(&tv, &time_waited);
449dfdcada3SDoug Rabson 		if (tv.tv_sec < 0 || tv.tv_usec < 0)
450dfdcada3SDoug Rabson 			tv.tv_sec = tv.tv_usec = 0;
451dfdcada3SDoug Rabson 
452dfdcada3SDoug Rabson 		error = msleep(&cr, &cs->cs_lock, cu->cu_waitflag,
453dfdcada3SDoug Rabson 		    cu->cu_waitchan, tvtohz(&tv));
454dfdcada3SDoug Rabson 
455dfdcada3SDoug Rabson 		if (!error) {
456dfdcada3SDoug Rabson 			/*
457dfdcada3SDoug Rabson 			 * We were woken up by the upcall.  If the
458dfdcada3SDoug Rabson 			 * upcall had a receive error, report that,
459dfdcada3SDoug Rabson 			 * otherwise we have a reply.
460dfdcada3SDoug Rabson 			 */
461dfdcada3SDoug Rabson 			if (cr.cr_error) {
462dfdcada3SDoug Rabson 				cu->cu_error.re_errno = cr.cr_error;
463dfdcada3SDoug Rabson 				cu->cu_error.re_status = RPC_CANTRECV;
464dfdcada3SDoug Rabson 				goto out;
465dfdcada3SDoug Rabson 			}
466dfdcada3SDoug Rabson 			break;
467dfdcada3SDoug Rabson 		}
468dfdcada3SDoug Rabson 
469dfdcada3SDoug Rabson 		/*
470dfdcada3SDoug Rabson 		 * The sleep returned an error so our request is still
471dfdcada3SDoug Rabson 		 * on the list. If we got EWOULDBLOCK, we may want to
472dfdcada3SDoug Rabson 		 * re-send the request.
473dfdcada3SDoug Rabson 		 */
474dfdcada3SDoug Rabson 		if (error != EWOULDBLOCK) {
475dfdcada3SDoug Rabson 			if (cr.cr_xid)
476dfdcada3SDoug Rabson 				TAILQ_REMOVE(&cs->cs_pending, &cr, cr_link);
477dfdcada3SDoug Rabson 			cu->cu_error.re_errno = error;
478dfdcada3SDoug Rabson 			if (error == EINTR)
479dfdcada3SDoug Rabson 				cu->cu_error.re_status = RPC_INTR;
480dfdcada3SDoug Rabson 			else
481dfdcada3SDoug Rabson 				cu->cu_error.re_status = RPC_CANTRECV;
482dfdcada3SDoug Rabson 			goto out;
483dfdcada3SDoug Rabson 		}
484dfdcada3SDoug Rabson 
485dfdcada3SDoug Rabson 		getmicrotime(&tv);
486dfdcada3SDoug Rabson 		time_waited = tv;
487dfdcada3SDoug Rabson 		timevalsub(&time_waited, &starttime);
488dfdcada3SDoug Rabson 
489dfdcada3SDoug Rabson 		/* Check for timeout. */
490dfdcada3SDoug Rabson 		if (timevalcmp(&time_waited, &timeout, >)) {
491dfdcada3SDoug Rabson 			if (cr.cr_xid)
492dfdcada3SDoug Rabson 				TAILQ_REMOVE(&cs->cs_pending, &cr, cr_link);
493dfdcada3SDoug Rabson 			cu->cu_error.re_errno = EWOULDBLOCK;
494dfdcada3SDoug Rabson 			cu->cu_error.re_status = RPC_TIMEDOUT;
495dfdcada3SDoug Rabson 			goto out;
496dfdcada3SDoug Rabson 		}
497dfdcada3SDoug Rabson 
498dfdcada3SDoug Rabson 		/* Retransmit if necessary. */
499dfdcada3SDoug Rabson 		if (timevalcmp(&time_waited, &next_sendtime, >)) {
500dfdcada3SDoug Rabson 			if (cr.cr_xid)
501dfdcada3SDoug Rabson 				TAILQ_REMOVE(&cs->cs_pending, &cr, cr_link);
502dfdcada3SDoug Rabson 			/* update retransmit_time */
503dfdcada3SDoug Rabson 			if (retransmit_time.tv_sec < RPC_MAX_BACKOFF)
504dfdcada3SDoug Rabson 				timevaladd(&retransmit_time, &retransmit_time);
505dfdcada3SDoug Rabson 			timevaladd(&next_sendtime, &retransmit_time);
506dfdcada3SDoug Rabson 			goto send_again;
507dfdcada3SDoug Rabson 		}
508dfdcada3SDoug Rabson 	}
509dfdcada3SDoug Rabson 
510dfdcada3SDoug Rabson got_reply:
511dfdcada3SDoug Rabson 	/*
512dfdcada3SDoug Rabson 	 * Now decode and validate the response. We need to drop the
513dfdcada3SDoug Rabson 	 * lock since xdr_replymsg may end up sleeping in malloc.
514dfdcada3SDoug Rabson 	 */
515dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
516dfdcada3SDoug Rabson 
517dfdcada3SDoug Rabson 	xdrmbuf_create(&xdrs, cr.cr_mrep, XDR_DECODE);
518dfdcada3SDoug Rabson 	ok = xdr_replymsg(&xdrs, &reply_msg);
519dfdcada3SDoug Rabson 	XDR_DESTROY(&xdrs);
520dfdcada3SDoug Rabson 	cr.cr_mrep = NULL;
521dfdcada3SDoug Rabson 
522dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
523dfdcada3SDoug Rabson 
524dfdcada3SDoug Rabson 	if (ok) {
525dfdcada3SDoug Rabson 		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
526dfdcada3SDoug Rabson 			(reply_msg.acpted_rply.ar_stat == SUCCESS))
527dfdcada3SDoug Rabson 			cu->cu_error.re_status = RPC_SUCCESS;
528dfdcada3SDoug Rabson 		else
529dfdcada3SDoug Rabson 			_seterr_reply(&reply_msg, &(cu->cu_error));
530dfdcada3SDoug Rabson 
531dfdcada3SDoug Rabson 		if (cu->cu_error.re_status == RPC_SUCCESS) {
532dfdcada3SDoug Rabson 			if (! AUTH_VALIDATE(cl->cl_auth,
533dfdcada3SDoug Rabson 					    &reply_msg.acpted_rply.ar_verf)) {
534dfdcada3SDoug Rabson 				cu->cu_error.re_status = RPC_AUTHERROR;
535dfdcada3SDoug Rabson 				cu->cu_error.re_why = AUTH_INVALIDRESP;
536dfdcada3SDoug Rabson 			}
537dfdcada3SDoug Rabson 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
538dfdcada3SDoug Rabson 				xdrs.x_op = XDR_FREE;
539dfdcada3SDoug Rabson 				(void) xdr_opaque_auth(&xdrs,
540dfdcada3SDoug Rabson 					&(reply_msg.acpted_rply.ar_verf));
541dfdcada3SDoug Rabson 			}
542dfdcada3SDoug Rabson 		}		/* end successful completion */
543dfdcada3SDoug Rabson 		/*
544dfdcada3SDoug Rabson 		 * If unsuccesful AND error is an authentication error
545dfdcada3SDoug Rabson 		 * then refresh credentials and try again, else break
546dfdcada3SDoug Rabson 		 */
547dfdcada3SDoug Rabson 		else if (cu->cu_error.re_status == RPC_AUTHERROR)
548dfdcada3SDoug Rabson 			/* maybe our credentials need to be refreshed ... */
549dfdcada3SDoug Rabson 			if (nrefreshes > 0 &&
550dfdcada3SDoug Rabson 			    AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
551dfdcada3SDoug Rabson 				nrefreshes--;
552dfdcada3SDoug Rabson 				goto call_again;
553dfdcada3SDoug Rabson 			}
554dfdcada3SDoug Rabson 		/* end of unsuccessful completion */
555dfdcada3SDoug Rabson 	}	/* end of valid reply message */
556dfdcada3SDoug Rabson 	else {
557dfdcada3SDoug Rabson 		cu->cu_error.re_status = RPC_CANTDECODERES;
558dfdcada3SDoug Rabson 
559dfdcada3SDoug Rabson 	}
560dfdcada3SDoug Rabson out:
561dfdcada3SDoug Rabson 	mtx_assert(&cs->cs_lock, MA_OWNED);
562dfdcada3SDoug Rabson 
563dfdcada3SDoug Rabson 	if (mreq)
564dfdcada3SDoug Rabson 		m_freem(mreq);
565dfdcada3SDoug Rabson 	if (cr.cr_mrep)
566dfdcada3SDoug Rabson 		m_freem(cr.cr_mrep);
567dfdcada3SDoug Rabson 
568dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
569dfdcada3SDoug Rabson 	return (cu->cu_error.re_status);
570dfdcada3SDoug Rabson }
571dfdcada3SDoug Rabson 
572dfdcada3SDoug Rabson static void
573dfdcada3SDoug Rabson clnt_dg_geterr(CLIENT *cl, struct rpc_err *errp)
574dfdcada3SDoug Rabson {
575dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
576dfdcada3SDoug Rabson 
577dfdcada3SDoug Rabson 	*errp = cu->cu_error;
578dfdcada3SDoug Rabson }
579dfdcada3SDoug Rabson 
580dfdcada3SDoug Rabson static bool_t
581dfdcada3SDoug Rabson clnt_dg_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
582dfdcada3SDoug Rabson {
583dfdcada3SDoug Rabson 	XDR xdrs;
584dfdcada3SDoug Rabson 	bool_t dummy;
585dfdcada3SDoug Rabson 
586dfdcada3SDoug Rabson 	xdrs.x_op = XDR_FREE;
587dfdcada3SDoug Rabson 	dummy = (*xdr_res)(&xdrs, res_ptr);
588dfdcada3SDoug Rabson 
589dfdcada3SDoug Rabson 	return (dummy);
590dfdcada3SDoug Rabson }
591dfdcada3SDoug Rabson 
592dfdcada3SDoug Rabson /*ARGSUSED*/
593dfdcada3SDoug Rabson static void
594dfdcada3SDoug Rabson clnt_dg_abort(CLIENT *h)
595dfdcada3SDoug Rabson {
596dfdcada3SDoug Rabson }
597dfdcada3SDoug Rabson 
598dfdcada3SDoug Rabson static bool_t
599dfdcada3SDoug Rabson clnt_dg_control(CLIENT *cl, u_int request, void *info)
600dfdcada3SDoug Rabson {
601dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
602dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
603dfdcada3SDoug Rabson 	struct sockaddr *addr;
604dfdcada3SDoug Rabson 
605dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
606dfdcada3SDoug Rabson 
607dfdcada3SDoug Rabson 	switch (request) {
608dfdcada3SDoug Rabson 	case CLSET_FD_CLOSE:
609dfdcada3SDoug Rabson 		cu->cu_closeit = TRUE;
610dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
611dfdcada3SDoug Rabson 		return (TRUE);
612dfdcada3SDoug Rabson 	case CLSET_FD_NCLOSE:
613dfdcada3SDoug Rabson 		cu->cu_closeit = FALSE;
614dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
615dfdcada3SDoug Rabson 		return (TRUE);
616dfdcada3SDoug Rabson 	}
617dfdcada3SDoug Rabson 
618dfdcada3SDoug Rabson 	/* for other requests which use info */
619dfdcada3SDoug Rabson 	if (info == NULL) {
620dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
621dfdcada3SDoug Rabson 		return (FALSE);
622dfdcada3SDoug Rabson 	}
623dfdcada3SDoug Rabson 	switch (request) {
624dfdcada3SDoug Rabson 	case CLSET_TIMEOUT:
625dfdcada3SDoug Rabson 		if (time_not_ok((struct timeval *)info)) {
626dfdcada3SDoug Rabson 			mtx_unlock(&cs->cs_lock);
627dfdcada3SDoug Rabson 			return (FALSE);
628dfdcada3SDoug Rabson 		}
629dfdcada3SDoug Rabson 		cu->cu_total = *(struct timeval *)info;
630dfdcada3SDoug Rabson 		break;
631dfdcada3SDoug Rabson 	case CLGET_TIMEOUT:
632dfdcada3SDoug Rabson 		*(struct timeval *)info = cu->cu_total;
633dfdcada3SDoug Rabson 		break;
634dfdcada3SDoug Rabson 	case CLSET_RETRY_TIMEOUT:
635dfdcada3SDoug Rabson 		if (time_not_ok((struct timeval *)info)) {
636dfdcada3SDoug Rabson 			mtx_unlock(&cs->cs_lock);
637dfdcada3SDoug Rabson 			return (FALSE);
638dfdcada3SDoug Rabson 		}
639dfdcada3SDoug Rabson 		cu->cu_wait = *(struct timeval *)info;
640dfdcada3SDoug Rabson 		break;
641dfdcada3SDoug Rabson 	case CLGET_RETRY_TIMEOUT:
642dfdcada3SDoug Rabson 		*(struct timeval *)info = cu->cu_wait;
643dfdcada3SDoug Rabson 		break;
644dfdcada3SDoug Rabson 	case CLGET_SVC_ADDR:
645dfdcada3SDoug Rabson 		/*
646dfdcada3SDoug Rabson 		 * Slightly different semantics to userland - we use
647dfdcada3SDoug Rabson 		 * sockaddr instead of netbuf.
648dfdcada3SDoug Rabson 		 */
649dfdcada3SDoug Rabson 		memcpy(info, &cu->cu_raddr, cu->cu_raddr.ss_len);
650dfdcada3SDoug Rabson 		break;
651dfdcada3SDoug Rabson 	case CLSET_SVC_ADDR:		/* set to new address */
652dfdcada3SDoug Rabson 		addr = (struct sockaddr *)info;
653dfdcada3SDoug Rabson 		(void) memcpy(&cu->cu_raddr, addr, addr->sa_len);
654dfdcada3SDoug Rabson 		break;
655dfdcada3SDoug Rabson 	case CLGET_XID:
656dfdcada3SDoug Rabson 		*(uint32_t *)info = cu->cu_xid;
657dfdcada3SDoug Rabson 		break;
658dfdcada3SDoug Rabson 
659dfdcada3SDoug Rabson 	case CLSET_XID:
660dfdcada3SDoug Rabson 		/* This will set the xid of the NEXT call */
661dfdcada3SDoug Rabson 		/* decrement by 1 as clnt_dg_call() increments once */
662dfdcada3SDoug Rabson 		cu->cu_xid = *(uint32_t *)info - 1;
663dfdcada3SDoug Rabson 		break;
664dfdcada3SDoug Rabson 
665dfdcada3SDoug Rabson 	case CLGET_VERS:
666dfdcada3SDoug Rabson 		/*
667dfdcada3SDoug Rabson 		 * This RELIES on the information that, in the call body,
668dfdcada3SDoug Rabson 		 * the version number field is the fifth field from the
669dfdcada3SDoug Rabson 		 * begining of the RPC header. MUST be changed if the
670dfdcada3SDoug Rabson 		 * call_struct is changed
671dfdcada3SDoug Rabson 		 */
672dfdcada3SDoug Rabson 		*(uint32_t *)info =
673dfdcada3SDoug Rabson 		    ntohl(*(uint32_t *)(void *)(cu->cu_mcallc +
674dfdcada3SDoug Rabson 		    4 * BYTES_PER_XDR_UNIT));
675dfdcada3SDoug Rabson 		break;
676dfdcada3SDoug Rabson 
677dfdcada3SDoug Rabson 	case CLSET_VERS:
678dfdcada3SDoug Rabson 		*(uint32_t *)(void *)(cu->cu_mcallc + 4 * BYTES_PER_XDR_UNIT)
679dfdcada3SDoug Rabson 			= htonl(*(uint32_t *)info);
680dfdcada3SDoug Rabson 		break;
681dfdcada3SDoug Rabson 
682dfdcada3SDoug Rabson 	case CLGET_PROG:
683dfdcada3SDoug Rabson 		/*
684dfdcada3SDoug Rabson 		 * This RELIES on the information that, in the call body,
685dfdcada3SDoug Rabson 		 * the program number field is the fourth field from the
686dfdcada3SDoug Rabson 		 * begining of the RPC header. MUST be changed if the
687dfdcada3SDoug Rabson 		 * call_struct is changed
688dfdcada3SDoug Rabson 		 */
689dfdcada3SDoug Rabson 		*(uint32_t *)info =
690dfdcada3SDoug Rabson 		    ntohl(*(uint32_t *)(void *)(cu->cu_mcallc +
691dfdcada3SDoug Rabson 		    3 * BYTES_PER_XDR_UNIT));
692dfdcada3SDoug Rabson 		break;
693dfdcada3SDoug Rabson 
694dfdcada3SDoug Rabson 	case CLSET_PROG:
695dfdcada3SDoug Rabson 		*(uint32_t *)(void *)(cu->cu_mcallc + 3 * BYTES_PER_XDR_UNIT)
696dfdcada3SDoug Rabson 			= htonl(*(uint32_t *)info);
697dfdcada3SDoug Rabson 		break;
698dfdcada3SDoug Rabson 	case CLSET_ASYNC:
699dfdcada3SDoug Rabson 		cu->cu_async = *(int *)info;
700dfdcada3SDoug Rabson 		break;
701dfdcada3SDoug Rabson 	case CLSET_CONNECT:
702dfdcada3SDoug Rabson 		cu->cu_connect = *(int *)info;
703dfdcada3SDoug Rabson 		break;
704dfdcada3SDoug Rabson 	case CLSET_WAITCHAN:
705dfdcada3SDoug Rabson 		cu->cu_waitchan = *(const char **)info;
706dfdcada3SDoug Rabson 		break;
707dfdcada3SDoug Rabson 	case CLGET_WAITCHAN:
708dfdcada3SDoug Rabson 		*(const char **) info = cu->cu_waitchan;
709dfdcada3SDoug Rabson 		break;
710dfdcada3SDoug Rabson 	case CLSET_INTERRUPTIBLE:
711dfdcada3SDoug Rabson 		if (*(int *) info)
712dfdcada3SDoug Rabson 			cu->cu_waitflag = PCATCH;
713dfdcada3SDoug Rabson 		else
714dfdcada3SDoug Rabson 			cu->cu_waitflag = 0;
715dfdcada3SDoug Rabson 		break;
716dfdcada3SDoug Rabson 	case CLGET_INTERRUPTIBLE:
717dfdcada3SDoug Rabson 		if (cu->cu_waitflag)
718dfdcada3SDoug Rabson 			*(int *) info = TRUE;
719dfdcada3SDoug Rabson 		else
720dfdcada3SDoug Rabson 			*(int *) info = FALSE;
721dfdcada3SDoug Rabson 		break;
722dfdcada3SDoug Rabson 	default:
723dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
724dfdcada3SDoug Rabson 		return (FALSE);
725dfdcada3SDoug Rabson 	}
726dfdcada3SDoug Rabson 	mtx_unlock(&cs->cs_lock);
727dfdcada3SDoug Rabson 	return (TRUE);
728dfdcada3SDoug Rabson }
729dfdcada3SDoug Rabson 
730dfdcada3SDoug Rabson static void
731dfdcada3SDoug Rabson clnt_dg_destroy(CLIENT *cl)
732dfdcada3SDoug Rabson {
733dfdcada3SDoug Rabson 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
734dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
735dfdcada3SDoug Rabson 	struct socket *so = NULL;
736dfdcada3SDoug Rabson 	bool_t lastsocketref;
737dfdcada3SDoug Rabson 
738dfdcada3SDoug Rabson 	SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
739dfdcada3SDoug Rabson 
740dfdcada3SDoug Rabson 	mtx_lock(&cs->cs_lock);
741dfdcada3SDoug Rabson 	cs->cs_refs--;
742dfdcada3SDoug Rabson 	if (cs->cs_refs == 0) {
743dfdcada3SDoug Rabson 		cu->cu_socket->so_upcallarg = NULL;
744dfdcada3SDoug Rabson 		cu->cu_socket->so_upcall = NULL;
745dfdcada3SDoug Rabson 		cu->cu_socket->so_rcv.sb_flags &= ~SB_UPCALL;
746dfdcada3SDoug Rabson 		mtx_destroy(&cs->cs_lock);
747dfdcada3SDoug Rabson 		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
748dfdcada3SDoug Rabson 		mem_free(cs, sizeof(*cs));
749dfdcada3SDoug Rabson 		lastsocketref = TRUE;
750dfdcada3SDoug Rabson 	} else {
751dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
752dfdcada3SDoug Rabson 		SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
753dfdcada3SDoug Rabson 		lastsocketref = FALSE;
754dfdcada3SDoug Rabson 	}
755dfdcada3SDoug Rabson 
756dfdcada3SDoug Rabson 	if (cu->cu_closeit) {
757dfdcada3SDoug Rabson 		KASSERT(lastsocketref, ("clnt_dg_destroy(): closing a socket "
758dfdcada3SDoug Rabson 			"shared with other clients"));
759dfdcada3SDoug Rabson 		so = cu->cu_socket;
760dfdcada3SDoug Rabson 		cu->cu_socket = NULL;
761dfdcada3SDoug Rabson 	}
762dfdcada3SDoug Rabson 
763dfdcada3SDoug Rabson 	if (so)
764dfdcada3SDoug Rabson 		soclose(so);
765dfdcada3SDoug Rabson 
766dfdcada3SDoug Rabson 	if (cl->cl_netid && cl->cl_netid[0])
767dfdcada3SDoug Rabson 		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
768dfdcada3SDoug Rabson 	if (cl->cl_tp && cl->cl_tp[0])
769dfdcada3SDoug Rabson 		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
770dfdcada3SDoug Rabson 	mem_free(cu, sizeof (*cu));
771dfdcada3SDoug Rabson 	mem_free(cl, sizeof (CLIENT));
772dfdcada3SDoug Rabson }
773dfdcada3SDoug Rabson 
774dfdcada3SDoug Rabson /*
775dfdcada3SDoug Rabson  * Make sure that the time is not garbage.  -1 value is allowed.
776dfdcada3SDoug Rabson  */
777dfdcada3SDoug Rabson static bool_t
778dfdcada3SDoug Rabson time_not_ok(struct timeval *t)
779dfdcada3SDoug Rabson {
780dfdcada3SDoug Rabson 	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
781dfdcada3SDoug Rabson 		t->tv_usec < -1 || t->tv_usec > 1000000);
782dfdcada3SDoug Rabson }
783dfdcada3SDoug Rabson 
784dfdcada3SDoug Rabson void
785dfdcada3SDoug Rabson clnt_dg_soupcall(struct socket *so, void *arg, int waitflag)
786dfdcada3SDoug Rabson {
787dfdcada3SDoug Rabson 	struct cu_socket *cs = (struct cu_socket *) arg;
788dfdcada3SDoug Rabson 	struct uio uio;
789dfdcada3SDoug Rabson 	struct mbuf *m;
790dfdcada3SDoug Rabson 	struct mbuf *control;
791dfdcada3SDoug Rabson 	struct cu_request *cr;
792dfdcada3SDoug Rabson 	int error, rcvflag, foundreq;
793dfdcada3SDoug Rabson 	uint32_t xid;
794dfdcada3SDoug Rabson 
795dfdcada3SDoug Rabson 	uio.uio_resid = 1000000000;
796dfdcada3SDoug Rabson 	uio.uio_td = curthread;
797dfdcada3SDoug Rabson 	do {
798dfdcada3SDoug Rabson 		m = NULL;
799dfdcada3SDoug Rabson 		control = NULL;
800dfdcada3SDoug Rabson 		rcvflag = MSG_DONTWAIT;
801dfdcada3SDoug Rabson 		error = soreceive(so, NULL, &uio, &m, &control, &rcvflag);
802dfdcada3SDoug Rabson 		if (control)
803dfdcada3SDoug Rabson 			m_freem(control);
804dfdcada3SDoug Rabson 
805dfdcada3SDoug Rabson 		if (error == EWOULDBLOCK)
806dfdcada3SDoug Rabson 			break;
807dfdcada3SDoug Rabson 
808dfdcada3SDoug Rabson 		/*
809dfdcada3SDoug Rabson 		 * If there was an error, wake up all pending
810dfdcada3SDoug Rabson 		 * requests.
811dfdcada3SDoug Rabson 		 */
812dfdcada3SDoug Rabson 		if (error) {
813dfdcada3SDoug Rabson 			mtx_lock(&cs->cs_lock);
814dfdcada3SDoug Rabson 			TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
815dfdcada3SDoug Rabson 				cr->cr_error = error;
816dfdcada3SDoug Rabson 				wakeup(cr);
817dfdcada3SDoug Rabson 			}
818dfdcada3SDoug Rabson 			TAILQ_INIT(&cs->cs_pending);
819dfdcada3SDoug Rabson 			mtx_unlock(&cs->cs_lock);
820dfdcada3SDoug Rabson 			break;
821dfdcada3SDoug Rabson 		}
822dfdcada3SDoug Rabson 
823dfdcada3SDoug Rabson 		/*
824dfdcada3SDoug Rabson 		 * The XID is in the first uint32_t of the reply.
825dfdcada3SDoug Rabson 		 */
826dfdcada3SDoug Rabson 		m = m_pullup(m, sizeof(xid));
827dfdcada3SDoug Rabson 		if (!m)
828dfdcada3SDoug Rabson 			break;
829dfdcada3SDoug Rabson 		xid = ntohl(*mtod(m, uint32_t *));
830dfdcada3SDoug Rabson 
831dfdcada3SDoug Rabson 		/*
832dfdcada3SDoug Rabson 		 * Attempt to match this reply with a pending request.
833dfdcada3SDoug Rabson 		 */
834dfdcada3SDoug Rabson 		mtx_lock(&cs->cs_lock);
835dfdcada3SDoug Rabson 		foundreq = 0;
836dfdcada3SDoug Rabson 		TAILQ_FOREACH(cr, &cs->cs_pending, cr_link) {
837dfdcada3SDoug Rabson 			if (cr->cr_xid == xid) {
838dfdcada3SDoug Rabson 				/*
839dfdcada3SDoug Rabson 				 * This one matches. We snip it out of
840dfdcada3SDoug Rabson 				 * the pending list and leave the
841dfdcada3SDoug Rabson 				 * reply mbuf in cr->cr_mrep. Set the
842dfdcada3SDoug Rabson 				 * XID to zero so that clnt_dg_call
843dfdcada3SDoug Rabson 				 * can know not to repeat the
844dfdcada3SDoug Rabson 				 * TAILQ_REMOVE.
845dfdcada3SDoug Rabson 				 */
846dfdcada3SDoug Rabson 				TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
847dfdcada3SDoug Rabson 				cr->cr_xid = 0;
848dfdcada3SDoug Rabson 				cr->cr_mrep = m;
849dfdcada3SDoug Rabson 				cr->cr_error = 0;
850dfdcada3SDoug Rabson 				foundreq = 1;
851dfdcada3SDoug Rabson 				wakeup(cr);
852dfdcada3SDoug Rabson 				break;
853dfdcada3SDoug Rabson 			}
854dfdcada3SDoug Rabson 		}
855dfdcada3SDoug Rabson 		mtx_unlock(&cs->cs_lock);
856dfdcada3SDoug Rabson 
857dfdcada3SDoug Rabson 		/*
858dfdcada3SDoug Rabson 		 * If we didn't find the matching request, just drop
859dfdcada3SDoug Rabson 		 * it - its probably a repeated reply.
860dfdcada3SDoug Rabson 		 */
861dfdcada3SDoug Rabson 		if (!foundreq)
862dfdcada3SDoug Rabson 			m_freem(m);
863dfdcada3SDoug Rabson 	} while (m);
864dfdcada3SDoug Rabson }
865dfdcada3SDoug Rabson 
866