clnt_dg.c revision 167199
1127668Sbms/*	$NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $	*/
256893Sfenner
356893Sfenner/*
456893Sfenner * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
575115Sfenner * unrestricted use provided that this legend is included on all tape
656893Sfenner * media and as a part of the software program in whole or part.  Users
756893Sfenner * may copy or modify Sun RPC without charge, but are not authorized
856893Sfenner * to license or distribute it to anyone else except as part of a product or
956893Sfenner * program developed by the user.
1056893Sfenner *
1156893Sfenner * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
1256893Sfenner * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
1356893Sfenner * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
1456893Sfenner *
1556893Sfenner * Sun RPC is provided with no support and without any obligation on the
1656893Sfenner * part of Sun Microsystems, Inc. to assist in its use, correction,
1756893Sfenner * modification or enhancement.
1856893Sfenner *
1956893Sfenner * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
2056893Sfenner * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
2156893Sfenner * OR ANY PART THEREOF.
2256893Sfenner *
23127668Sbms * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24127668Sbms * or profits or other special, indirect and consequential damages, even if
25127668Sbms * Sun has been advised of the possibility of such damages.
2698524Sfenner *
2798524Sfenner * Sun Microsystems, Inc.
2875115Sfenner * 2550 Garcia Avenue
29127668Sbms * Mountain View, California  94043
30127668Sbms */
31127668Sbms/*
32127668Sbms * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33127668Sbms */
34127668Sbms
35127668Sbms#if defined(LIBC_SCCS) && !defined(lint)
36127668Sbms#ident	"@(#)clnt_dg.c	1.23	94/04/22 SMI"
37127668Sbmsstatic char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
38127668Sbms#endif
39127668Sbms#include <sys/cdefs.h>
40127668Sbms__FBSDID("$FreeBSD: head/lib/libc/rpc/clnt_dg.c 167199 2007-03-04 12:25:03Z simon $");
41127668Sbms
42127668Sbms/*
43127668Sbms * Implements a connectionless client side RPC.
44127668Sbms */
45127668Sbms
46127668Sbms#include "namespace.h"
47127668Sbms#include "reentrant.h"
48127668Sbms#include <sys/types.h>
49127668Sbms#include <sys/event.h>
50147899Ssam#include <sys/time.h>
51147899Ssam#include <sys/socket.h>
52147899Ssam#include <sys/ioctl.h>
53146773Ssam#include <arpa/inet.h>
54146773Ssam#include <rpc/rpc.h>
55146773Ssam#include <errno.h>
56146773Ssam#include <stdlib.h>
57146773Ssam#include <string.h>
58146773Ssam#include <signal.h>
59146773Ssam#include <unistd.h>
60146773Ssam#include <err.h>
61146773Ssam#include "un-namespace.h"
6298524Sfenner#include "rpc_com.h"
6398524Sfenner#include "mt_misc.h"
6498524Sfenner
65127668Sbms
66127668Sbms#ifdef _FREEFALL_CONFIG
6756893Sfenner/*
68127668Sbms * Disable RPC exponential back-off for FreeBSD.org systems.
69127668Sbms */
7056893Sfenner#define	RPC_MAX_BACKOFF		1 /* second */
71146773Ssam#else
72146773Ssam#define	RPC_MAX_BACKOFF		30 /* seconds */
73146773Ssam#endif
74146773Ssam
75146773Ssam
76146773Ssamstatic struct clnt_ops *clnt_dg_ops(void);
77146773Ssamstatic bool_t time_not_ok(struct timeval *);
78146773Ssamstatic enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
79146773Ssam	    xdrproc_t, void *, struct timeval);
80127668Sbmsstatic void clnt_dg_geterr(CLIENT *, struct rpc_err *);
8175115Sfennerstatic bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
8275115Sfennerstatic void clnt_dg_abort(CLIENT *);
83146773Ssamstatic bool_t clnt_dg_control(CLIENT *, u_int, void *);
84146773Ssamstatic void clnt_dg_destroy(CLIENT *);
85146773Ssam
86146773Ssam
87127668Sbms
8856893Sfenner
8956893Sfenner/*
90127668Sbms *	This machinery implements per-fd locks for MT-safety.  It is not
91127668Sbms *	sufficient to do per-CLIENT handle locks for MT-safety because a
92127668Sbms *	user may create more than one CLIENT handle with the same fd behind
93235530Sdelphij *	it.  Therfore, we allocate an array of flags (dg_fd_locks), protected
94235530Sdelphij *	by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
95235530Sdelphij *	similarly protected.  Dg_fd_lock[fd] == 1 => a call is activte on some
96127668Sbms *	CLIENT handle created for that fd.
9756893Sfenner *	The current implementation holds locks across the entire RPC and reply,
9856893Sfenner *	including retransmissions.  Yes, this is silly, and as soon as this
99127668Sbms *	code is proven to work, this should be the first thing fixed.  One step
100127668Sbms *	at a time.
10156893Sfenner */
102127668Sbmsstatic int	*dg_fd_locks;
103127668Sbmsstatic cond_t	*dg_cv;
10456893Sfenner#define	release_fd_lock(fd, mask) {		\
105127668Sbms	mutex_lock(&clnt_fd_lock);	\
106127668Sbms	dg_fd_locks[fd] = 0;		\
10756893Sfenner	mutex_unlock(&clnt_fd_lock);	\
108127668Sbms	thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
109127668Sbms	cond_signal(&dg_cv[fd]);	\
110127668Sbms}
111127668Sbms
112127668Sbmsstatic const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
113127668Sbms
114127668Sbms/* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */
115127668Sbms
116127668Sbms/*
117127668Sbms * Private data kept per client handle
118127668Sbms */
119127668Sbmsstruct cu_data {
120127668Sbms	int			cu_fd;		/* connections fd */
121127668Sbms	bool_t			cu_closeit;	/* opened by library */
122127668Sbms	struct sockaddr_storage	cu_raddr;	/* remote address */
123172683Smlaier	int			cu_rlen;
124172683Smlaier	struct timeval		cu_wait;	/* retransmit interval */
125172683Smlaier	struct timeval		cu_total;	/* total time for the call */
126127668Sbms	struct rpc_err		cu_error;
127127668Sbms	XDR			cu_outxdrs;
128127668Sbms	u_int			cu_xdrpos;
129214478Srpaulo	u_int			cu_sendsz;	/* send size */
130214478Srpaulo	char			*cu_outbuf;
131214478Srpaulo	u_int			cu_recvsz;	/* recv size */
132190207Srpaulo	int			cu_async;
133190207Srpaulo	int			cu_connect;	/* Use connect(). */
134190207Srpaulo	int			cu_connected;	/* Have done connect(). */
135127668Sbms	struct kevent		cu_kin;
136127668Sbms	int			cu_kq;
137127668Sbms	char			cu_inbuf[1];
138190207Srpaulo};
139190207Srpaulo
140190207Srpaulo/*
141127668Sbms * Connection less client creation returns with client handle parameters.
142127668Sbms * Default options are set, which the user can change using clnt_control().
143127668Sbms * fd should be open and bound.
144127668Sbms * NB: The rpch->cl_auth is initialized to null authentication.
145127668Sbms * 	Caller may wish to set this something more useful.
146127668Sbms *
147127668Sbms * sendsz and recvsz are the maximum allowable packet sizes that can be
148127668Sbms * sent and received. Normally they are the same, but they can be
149127668Sbms * changed to improve the program efficiency and buffer allocation.
150127668Sbms * If they are 0, use the transport default.
151127668Sbms *
152127668Sbms * If svcaddr is NULL, returns NULL.
153235530Sdelphij */
154235530SdelphijCLIENT *
155235530Sdelphijclnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
156214478Srpaulo	int fd;				/* open file descriptor */
157214478Srpaulo	const struct netbuf *svcaddr;	/* servers address */
158214478Srpaulo	rpcprog_t program;		/* program number */
159127668Sbms	rpcvers_t version;		/* version number */
16056893Sfenner	u_int sendsz;			/* buffer recv size */
16156893Sfenner	u_int recvsz;			/* buffer send size */
162127668Sbms{
163127668Sbms	CLIENT *cl = NULL;		/* client handle */
164127668Sbms	struct cu_data *cu = NULL;	/* private data */
165235530Sdelphij	struct timeval now;
166235530Sdelphij	struct rpc_msg call_msg;
167235530Sdelphij	sigset_t mask;
168127668Sbms	sigset_t newmask;
16956893Sfenner	struct __rpc_sockinfo si;
17056893Sfenner	int one = 1;
171127668Sbms
17256893Sfenner	sigfillset(&newmask);
17356893Sfenner	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
174127668Sbms	mutex_lock(&clnt_fd_lock);
17556893Sfenner	if (dg_fd_locks == (int *) NULL) {
17656893Sfenner		int cv_allocsz;
177127668Sbms		size_t fd_allocsz;
178127668Sbms		int dtbsize = __rpc_dtbsize();
179127668Sbms
180127668Sbms		fd_allocsz = dtbsize * sizeof (int);
18175115Sfenner		dg_fd_locks = (int *) mem_alloc(fd_allocsz);
18275115Sfenner		if (dg_fd_locks == (int *) NULL) {
183214478Srpaulo			mutex_unlock(&clnt_fd_lock);
184214478Srpaulo			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
185214478Srpaulo			goto err1;
186127668Sbms		} else
187127668Sbms			memset(dg_fd_locks, '\0', fd_allocsz);
188127668Sbms
189127668Sbms		cv_allocsz = dtbsize * sizeof (cond_t);
190127668Sbms		dg_cv = (cond_t *) mem_alloc(cv_allocsz);
191127668Sbms		if (dg_cv == (cond_t *) NULL) {
192127668Sbms			mem_free(dg_fd_locks, fd_allocsz);
19356893Sfenner			dg_fd_locks = (int *) NULL;
19456893Sfenner			mutex_unlock(&clnt_fd_lock);
195127668Sbms			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
19698524Sfenner			goto err1;
19798524Sfenner		} else {
198127668Sbms			int i;
199127668Sbms
200127668Sbms			for (i = 0; i < dtbsize; i++)
201127668Sbms				cond_init(&dg_cv[i], 0, (void *) 0);
202127668Sbms		}
203127668Sbms	}
204127668Sbms
205127668Sbms	mutex_unlock(&clnt_fd_lock);
206127668Sbms	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
207127668Sbms
20875115Sfenner	if (svcaddr == NULL) {
20975115Sfenner		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
210127668Sbms		return (NULL);
21175115Sfenner	}
21275115Sfenner
213127668Sbms	if (!__rpc_fd2sockinfo(fd, &si)) {
214127668Sbms		rpc_createerr.cf_stat = RPC_TLIERROR;
215127668Sbms		rpc_createerr.cf_error.re_errno = 0;
216147899Ssam		return (NULL);
217147899Ssam	}
218147899Ssam	/*
219147899Ssam	 * Find the receive and the send size
220147899Ssam	 */
221147899Ssam	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
222127668Sbms	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
223127668Sbms	if ((sendsz == 0) || (recvsz == 0)) {
224127668Sbms		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
225127668Sbms		rpc_createerr.cf_error.re_errno = 0;
226127668Sbms		return (NULL);
227127668Sbms	}
228127668Sbms
229127668Sbms	if ((cl = mem_alloc(sizeof (CLIENT))) == NULL)
230127668Sbms		goto err1;
231235530Sdelphij	/*
232235530Sdelphij	 * Should be multiple of 4 for XDR.
233235530Sdelphij	 */
234127668Sbms	sendsz = ((sendsz + 3) / 4) * 4;
23556893Sfenner	recvsz = ((recvsz + 3) / 4) * 4;
23656893Sfenner	cu = mem_alloc(sizeof (*cu) + sendsz + recvsz);
237127668Sbms	if (cu == NULL)
23875115Sfenner		goto err1;
23975115Sfenner	(void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len);
240127668Sbms	cu->cu_rlen = svcaddr->len;
241127668Sbms	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
24256893Sfenner	/* Other values can also be set through clnt_control() */
243214478Srpaulo	cu->cu_wait.tv_sec = 15;	/* heuristically chosen */
244214478Srpaulo	cu->cu_wait.tv_usec = 0;
245214478Srpaulo	cu->cu_total.tv_sec = -1;
246146773Ssam	cu->cu_total.tv_usec = -1;
247146773Ssam	cu->cu_sendsz = sendsz;
248146773Ssam	cu->cu_recvsz = recvsz;
249146773Ssam	cu->cu_async = FALSE;
250146773Ssam	cu->cu_connect = FALSE;
251146773Ssam	cu->cu_connected = FALSE;
252127668Sbms	(void) gettimeofday(&now, NULL);
253127668Sbms	call_msg.rm_xid = __RPC_GETXID(&now);
25456893Sfenner	call_msg.rm_call.cb_prog = program;
255127668Sbms	call_msg.rm_call.cb_vers = version;
256127668Sbms	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
25756893Sfenner	if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
258127668Sbms		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
259127668Sbms		rpc_createerr.cf_error.re_errno = 0;
26056893Sfenner		goto err2;
261127668Sbms	}
262127668Sbms	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
26356893Sfenner
264127668Sbms	/* XXX fvdl - do we still want this? */
265127668Sbms#if 0
26656893Sfenner	(void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf);
267127668Sbms#endif
268127668Sbms	_ioctl(fd, FIONBIO, (char *)(void *)&one);
26956893Sfenner
270214478Srpaulo	/*
271214478Srpaulo	 * By default, closeit is always FALSE. It is users responsibility
272214478Srpaulo	 * to do a close on it, else the user may use clnt_control
273127668Sbms	 * to let clnt_destroy do it for him/her.
274127668Sbms	 */
275127668Sbms	cu->cu_closeit = FALSE;
276127668Sbms	cu->cu_fd = fd;
277127668Sbms	cl->cl_ops = clnt_dg_ops();
278127668Sbms	cl->cl_private = (caddr_t)(void *)cu;
279214478Srpaulo	cl->cl_auth = authnone_create();
280214478Srpaulo	cl->cl_tp = NULL;
281214478Srpaulo	cl->cl_netid = NULL;
282214478Srpaulo	cu->cu_kq = -1;
283214478Srpaulo	EV_SET(&cu->cu_kin, cu->cu_fd, EVFILT_READ, EV_ADD, 0, 0, 0);
284214478Srpaulo	return (cl);
285235530Sdelphijerr1:
286235530Sdelphij	warnx(mem_err_clnt_dg);
287235530Sdelphij	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
288214478Srpaulo	rpc_createerr.cf_error.re_errno = errno;
289214478Srpauloerr2:
290214478Srpaulo	if (cl) {
291214478Srpaulo		mem_free(cl, sizeof (CLIENT));
292214478Srpaulo		if (cu)
293214478Srpaulo			mem_free(cu, sizeof (*cu) + sendsz + recvsz);
294214478Srpaulo	}
295111726Sfenner	return (NULL);
296111726Sfenner}
297147899Ssam
298147899Ssamstatic enum clnt_stat
299147899Ssamclnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
300147899Ssam	CLIENT	*cl;			/* client handle */
301147899Ssam	rpcproc_t	proc;		/* procedure number */
302147899Ssam	xdrproc_t	xargs;		/* xdr routine for args */
303147899Ssam	void		*argsp;		/* pointer to args */
304147899Ssam	xdrproc_t	xresults;	/* xdr routine for results */
305147899Ssam	void		*resultsp;	/* pointer to results */
306147899Ssam	struct timeval	utimeout;	/* seconds to wait before giving up */
307147899Ssam{
308147899Ssam	struct cu_data *cu = (struct cu_data *)cl->cl_private;
309147899Ssam	XDR *xdrs;
310147899Ssam	size_t outlen = 0;
311147899Ssam	struct rpc_msg reply_msg;
312147899Ssam	XDR reply_xdrs;
313147899Ssam	bool_t ok;
314147899Ssam	int nrefreshes = 2;		/* number of times to refresh cred */
315147899Ssam	struct timeval timeout;
316147899Ssam	struct timeval retransmit_time;
317147899Ssam	struct timeval next_sendtime, starttime, time_waited, tv;
318147899Ssam	struct timespec ts;
319147899Ssam	struct kevent kv;
320147899Ssam	struct sockaddr *sa;
321	sigset_t mask;
322	sigset_t newmask;
323	socklen_t inlen, salen;
324	ssize_t recvlen = 0;
325	int kin_len, n, rpc_lock_value;
326	u_int32_t xid;
327
328	outlen = 0;
329	sigfillset(&newmask);
330	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
331	mutex_lock(&clnt_fd_lock);
332	while (dg_fd_locks[cu->cu_fd])
333		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
334	if (__isthreaded)
335		rpc_lock_value = 1;
336	else
337		rpc_lock_value = 0;
338	dg_fd_locks[cu->cu_fd] = rpc_lock_value;
339	mutex_unlock(&clnt_fd_lock);
340	if (cu->cu_total.tv_usec == -1) {
341		timeout = utimeout;	/* use supplied timeout */
342	} else {
343		timeout = cu->cu_total;	/* use default timeout */
344	}
345
346	if (cu->cu_connect && !cu->cu_connected) {
347		if (_connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr,
348		    cu->cu_rlen) < 0) {
349			cu->cu_error.re_errno = errno;
350			cu->cu_error.re_status = RPC_CANTSEND;
351			goto out;
352		}
353		cu->cu_connected = 1;
354	}
355	if (cu->cu_connected) {
356		sa = NULL;
357		salen = 0;
358	} else {
359		sa = (struct sockaddr *)&cu->cu_raddr;
360		salen = cu->cu_rlen;
361	}
362	time_waited.tv_sec = 0;
363	time_waited.tv_usec = 0;
364	retransmit_time = next_sendtime = cu->cu_wait;
365	gettimeofday(&starttime, NULL);
366
367	/* Clean up in case the last call ended in a longjmp(3) call. */
368	if (cu->cu_kq >= 0)
369		_close(cu->cu_kq);
370	if ((cu->cu_kq = kqueue()) < 0) {
371		cu->cu_error.re_errno = errno;
372		cu->cu_error.re_status = RPC_CANTSEND;
373		goto out;
374	}
375	kin_len = 1;
376
377call_again:
378	xdrs = &(cu->cu_outxdrs);
379	if (cu->cu_async == TRUE && xargs == NULL)
380		goto get_reply;
381	xdrs->x_op = XDR_ENCODE;
382	XDR_SETPOS(xdrs, cu->cu_xdrpos);
383	/*
384	 * the transaction is the first thing in the out buffer
385	 * XXX Yes, and it's in network byte order, so we should to
386	 * be careful when we increment it, shouldn't we.
387	 */
388	xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf));
389	xid++;
390	*(u_int32_t *)(void *)(cu->cu_outbuf) = htonl(xid);
391
392	if ((! XDR_PUTINT32(xdrs, &proc)) ||
393	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
394	    (! (*xargs)(xdrs, argsp))) {
395		cu->cu_error.re_status = RPC_CANTENCODEARGS;
396		goto out;
397	}
398	outlen = (size_t)XDR_GETPOS(xdrs);
399
400send_again:
401	if (_sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) {
402		cu->cu_error.re_errno = errno;
403		cu->cu_error.re_status = RPC_CANTSEND;
404		goto out;
405	}
406
407	/*
408	 * Hack to provide rpc-based message passing
409	 */
410	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
411		cu->cu_error.re_status = RPC_TIMEDOUT;
412		goto out;
413	}
414
415get_reply:
416
417	/*
418	 * sub-optimal code appears here because we have
419	 * some clock time to spare while the packets are in flight.
420	 * (We assume that this is actually only executed once.)
421	 */
422	reply_msg.acpted_rply.ar_verf = _null_auth;
423	reply_msg.acpted_rply.ar_results.where = resultsp;
424	reply_msg.acpted_rply.ar_results.proc = xresults;
425
426	for (;;) {
427		/* Decide how long to wait. */
428		if (timercmp(&next_sendtime, &timeout, <))
429			timersub(&next_sendtime, &time_waited, &tv);
430		else
431			timersub(&timeout, &time_waited, &tv);
432		if (tv.tv_sec < 0 || tv.tv_usec < 0)
433			tv.tv_sec = tv.tv_usec = 0;
434		TIMEVAL_TO_TIMESPEC(&tv, &ts);
435
436		n = _kevent(cu->cu_kq, &cu->cu_kin, kin_len, &kv, 1, &ts);
437		/* We don't need to register the event again. */
438		kin_len = 0;
439
440		if (n == 1) {
441			if (kv.flags & EV_ERROR) {
442				cu->cu_error.re_errno = kv.data;
443				cu->cu_error.re_status = RPC_CANTRECV;
444				goto out;
445			}
446			/* We have some data now */
447			do {
448				recvlen = _recvfrom(cu->cu_fd, cu->cu_inbuf,
449				    cu->cu_recvsz, 0, NULL, NULL);
450			} while (recvlen < 0 && errno == EINTR);
451			if (recvlen < 0 && errno != EWOULDBLOCK) {
452				cu->cu_error.re_errno = errno;
453				cu->cu_error.re_status = RPC_CANTRECV;
454				goto out;
455			}
456			if (recvlen >= sizeof(u_int32_t) &&
457			    (cu->cu_async == TRUE ||
458			    *((u_int32_t *)(void *)(cu->cu_inbuf)) ==
459			    *((u_int32_t *)(void *)(cu->cu_outbuf)))) {
460				/* We now assume we have the proper reply. */
461				break;
462			}
463		}
464		if (n == -1 && errno != EINTR) {
465			cu->cu_error.re_errno = errno;
466			cu->cu_error.re_status = RPC_CANTRECV;
467			goto out;
468		}
469		gettimeofday(&tv, NULL);
470		timersub(&tv, &starttime, &time_waited);
471
472		/* Check for timeout. */
473		if (timercmp(&time_waited, &timeout, >)) {
474			cu->cu_error.re_status = RPC_TIMEDOUT;
475			goto out;
476		}
477
478		/* Retransmit if necessary. */
479		if (timercmp(&time_waited, &next_sendtime, >)) {
480			/* update retransmit_time */
481			if (retransmit_time.tv_sec < RPC_MAX_BACKOFF)
482				timeradd(&retransmit_time, &retransmit_time,
483				    &retransmit_time);
484			timeradd(&next_sendtime, &retransmit_time,
485			    &next_sendtime);
486			goto send_again;
487		}
488	}
489	inlen = (socklen_t)recvlen;
490
491	/*
492	 * now decode and validate the response
493	 */
494
495	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
496	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
497	/* XDR_DESTROY(&reply_xdrs);	save a few cycles on noop destroy */
498	if (ok) {
499		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
500			(reply_msg.acpted_rply.ar_stat == SUCCESS))
501			cu->cu_error.re_status = RPC_SUCCESS;
502		else
503			_seterr_reply(&reply_msg, &(cu->cu_error));
504
505		if (cu->cu_error.re_status == RPC_SUCCESS) {
506			if (! AUTH_VALIDATE(cl->cl_auth,
507					    &reply_msg.acpted_rply.ar_verf)) {
508				cu->cu_error.re_status = RPC_AUTHERROR;
509				cu->cu_error.re_why = AUTH_INVALIDRESP;
510			}
511			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
512				xdrs->x_op = XDR_FREE;
513				(void) xdr_opaque_auth(xdrs,
514					&(reply_msg.acpted_rply.ar_verf));
515			}
516		}		/* end successful completion */
517		/*
518		 * If unsuccesful AND error is an authentication error
519		 * then refresh credentials and try again, else break
520		 */
521		else if (cu->cu_error.re_status == RPC_AUTHERROR)
522			/* maybe our credentials need to be refreshed ... */
523			if (nrefreshes > 0 &&
524			    AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
525				nrefreshes--;
526				goto call_again;
527			}
528		/* end of unsuccessful completion */
529	}	/* end of valid reply message */
530	else {
531		cu->cu_error.re_status = RPC_CANTDECODERES;
532
533	}
534out:
535	if (cu->cu_kq >= 0)
536		_close(cu->cu_kq);
537	cu->cu_kq = -1;
538	release_fd_lock(cu->cu_fd, mask);
539	return (cu->cu_error.re_status);
540}
541
542static void
543clnt_dg_geterr(cl, errp)
544	CLIENT *cl;
545	struct rpc_err *errp;
546{
547	struct cu_data *cu = (struct cu_data *)cl->cl_private;
548
549	*errp = cu->cu_error;
550}
551
552static bool_t
553clnt_dg_freeres(cl, xdr_res, res_ptr)
554	CLIENT *cl;
555	xdrproc_t xdr_res;
556	void *res_ptr;
557{
558	struct cu_data *cu = (struct cu_data *)cl->cl_private;
559	XDR *xdrs = &(cu->cu_outxdrs);
560	bool_t dummy;
561	sigset_t mask;
562	sigset_t newmask;
563
564	sigfillset(&newmask);
565	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
566	mutex_lock(&clnt_fd_lock);
567	while (dg_fd_locks[cu->cu_fd])
568		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
569	xdrs->x_op = XDR_FREE;
570	dummy = (*xdr_res)(xdrs, res_ptr);
571	mutex_unlock(&clnt_fd_lock);
572	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
573	cond_signal(&dg_cv[cu->cu_fd]);
574	return (dummy);
575}
576
577/*ARGSUSED*/
578static void
579clnt_dg_abort(h)
580	CLIENT *h;
581{
582}
583
584static bool_t
585clnt_dg_control(cl, request, info)
586	CLIENT *cl;
587	u_int request;
588	void *info;
589{
590	struct cu_data *cu = (struct cu_data *)cl->cl_private;
591	struct netbuf *addr;
592	sigset_t mask;
593	sigset_t newmask;
594	int rpc_lock_value;
595
596	sigfillset(&newmask);
597	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
598	mutex_lock(&clnt_fd_lock);
599	while (dg_fd_locks[cu->cu_fd])
600		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
601	if (__isthreaded)
602                rpc_lock_value = 1;
603        else
604                rpc_lock_value = 0;
605	dg_fd_locks[cu->cu_fd] = rpc_lock_value;
606	mutex_unlock(&clnt_fd_lock);
607	switch (request) {
608	case CLSET_FD_CLOSE:
609		cu->cu_closeit = TRUE;
610		release_fd_lock(cu->cu_fd, mask);
611		return (TRUE);
612	case CLSET_FD_NCLOSE:
613		cu->cu_closeit = FALSE;
614		release_fd_lock(cu->cu_fd, mask);
615		return (TRUE);
616	}
617
618	/* for other requests which use info */
619	if (info == NULL) {
620		release_fd_lock(cu->cu_fd, mask);
621		return (FALSE);
622	}
623	switch (request) {
624	case CLSET_TIMEOUT:
625		if (time_not_ok((struct timeval *)info)) {
626			release_fd_lock(cu->cu_fd, mask);
627			return (FALSE);
628		}
629		cu->cu_total = *(struct timeval *)info;
630		break;
631	case CLGET_TIMEOUT:
632		*(struct timeval *)info = cu->cu_total;
633		break;
634	case CLGET_SERVER_ADDR:		/* Give him the fd address */
635		/* Now obsolete. Only for backward compatibility */
636		(void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen);
637		break;
638	case CLSET_RETRY_TIMEOUT:
639		if (time_not_ok((struct timeval *)info)) {
640			release_fd_lock(cu->cu_fd, mask);
641			return (FALSE);
642		}
643		cu->cu_wait = *(struct timeval *)info;
644		break;
645	case CLGET_RETRY_TIMEOUT:
646		*(struct timeval *)info = cu->cu_wait;
647		break;
648	case CLGET_FD:
649		*(int *)info = cu->cu_fd;
650		break;
651	case CLGET_SVC_ADDR:
652		addr = (struct netbuf *)info;
653		addr->buf = &cu->cu_raddr;
654		addr->len = cu->cu_rlen;
655		addr->maxlen = sizeof cu->cu_raddr;
656		break;
657	case CLSET_SVC_ADDR:		/* set to new address */
658		addr = (struct netbuf *)info;
659		if (addr->len < sizeof cu->cu_raddr) {
660			release_fd_lock(cu->cu_fd, mask);
661			return (FALSE);
662		}
663		(void) memcpy(&cu->cu_raddr, addr->buf, addr->len);
664		cu->cu_rlen = addr->len;
665		break;
666	case CLGET_XID:
667		/*
668		 * use the knowledge that xid is the
669		 * first element in the call structure *.
670		 * This will get the xid of the PREVIOUS call
671		 */
672		*(u_int32_t *)info =
673		    ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
674		break;
675
676	case CLSET_XID:
677		/* This will set the xid of the NEXT call */
678		*(u_int32_t *)(void *)cu->cu_outbuf =
679		    htonl(*(u_int32_t *)info - 1);
680		/* decrement by 1 as clnt_dg_call() increments once */
681		break;
682
683	case CLGET_VERS:
684		/*
685		 * This RELIES on the information that, in the call body,
686		 * the version number field is the fifth field from the
687		 * begining of the RPC header. MUST be changed if the
688		 * call_struct is changed
689		 */
690		*(u_int32_t *)info =
691		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
692		    4 * BYTES_PER_XDR_UNIT));
693		break;
694
695	case CLSET_VERS:
696		*(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
697			= htonl(*(u_int32_t *)info);
698		break;
699
700	case CLGET_PROG:
701		/*
702		 * This RELIES on the information that, in the call body,
703		 * the program number field is the fourth field from the
704		 * begining of the RPC header. MUST be changed if the
705		 * call_struct is changed
706		 */
707		*(u_int32_t *)info =
708		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
709		    3 * BYTES_PER_XDR_UNIT));
710		break;
711
712	case CLSET_PROG:
713		*(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
714			= htonl(*(u_int32_t *)info);
715		break;
716	case CLSET_ASYNC:
717		cu->cu_async = *(int *)info;
718		break;
719	case CLSET_CONNECT:
720		cu->cu_connect = *(int *)info;
721		break;
722	default:
723		release_fd_lock(cu->cu_fd, mask);
724		return (FALSE);
725	}
726	release_fd_lock(cu->cu_fd, mask);
727	return (TRUE);
728}
729
730static void
731clnt_dg_destroy(cl)
732	CLIENT *cl;
733{
734	struct cu_data *cu = (struct cu_data *)cl->cl_private;
735	int cu_fd = cu->cu_fd;
736	sigset_t mask;
737	sigset_t newmask;
738
739	sigfillset(&newmask);
740	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
741	mutex_lock(&clnt_fd_lock);
742	while (dg_fd_locks[cu_fd])
743		cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
744	if (cu->cu_closeit)
745		(void)_close(cu_fd);
746	if (cu->cu_kq >= 0)
747		_close(cu->cu_kq);
748	XDR_DESTROY(&(cu->cu_outxdrs));
749	mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
750	if (cl->cl_netid && cl->cl_netid[0])
751		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
752	if (cl->cl_tp && cl->cl_tp[0])
753		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
754	mem_free(cl, sizeof (CLIENT));
755	mutex_unlock(&clnt_fd_lock);
756	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
757	cond_signal(&dg_cv[cu_fd]);
758}
759
760static struct clnt_ops *
761clnt_dg_ops()
762{
763	static struct clnt_ops ops;
764	sigset_t mask;
765	sigset_t newmask;
766
767/* VARIABLES PROTECTED BY ops_lock: ops */
768
769	sigfillset(&newmask);
770	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
771	mutex_lock(&ops_lock);
772	if (ops.cl_call == NULL) {
773		ops.cl_call = clnt_dg_call;
774		ops.cl_abort = clnt_dg_abort;
775		ops.cl_geterr = clnt_dg_geterr;
776		ops.cl_freeres = clnt_dg_freeres;
777		ops.cl_destroy = clnt_dg_destroy;
778		ops.cl_control = clnt_dg_control;
779	}
780	mutex_unlock(&ops_lock);
781	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
782	return (&ops);
783}
784
785/*
786 * Make sure that the time is not garbage.  -1 value is allowed.
787 */
788static bool_t
789time_not_ok(t)
790	struct timeval *t;
791{
792	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
793		t->tv_usec < -1 || t->tv_usec > 1000000);
794}
795
796