xref: /freebsd/lib/libc/rpc/clnt_dg.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /*	$NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $	*/
2 
3 /*
4  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5  * unrestricted use provided that this legend is included on all tape
6  * media and as a part of the software program in whole or part.  Users
7  * may copy or modify Sun RPC without charge, but are not authorized
8  * to license or distribute it to anyone else except as part of a product or
9  * program developed by the user.
10  *
11  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14  *
15  * Sun RPC is provided with no support and without any obligation on the
16  * part of Sun Microsystems, Inc. to assist in its use, correction,
17  * modification or enhancement.
18  *
19  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21  * OR ANY PART THEREOF.
22  *
23  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24  * or profits or other special, indirect and consequential damages, even if
25  * Sun has been advised of the possibility of such damages.
26  *
27  * Sun Microsystems, Inc.
28  * 2550 Garcia Avenue
29  * Mountain View, California  94043
30  */
31 /*
32  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33  */
34 
35 #if defined(LIBC_SCCS) && !defined(lint)
36 #ident	"@(#)clnt_dg.c	1.23	94/04/22 SMI"
37 static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
38 #endif
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 /*
43  * Implements a connectionless client side RPC.
44  */
45 
46 #include "namespace.h"
47 #include "reentrant.h"
48 #include <sys/types.h>
49 #include <sys/event.h>
50 #include <sys/time.h>
51 #include <sys/socket.h>
52 #include <sys/ioctl.h>
53 #include <arpa/inet.h>
54 #include <rpc/rpc.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <signal.h>
59 #include <unistd.h>
60 #include <err.h>
61 #include "un-namespace.h"
62 #include "rpc_com.h"
63 #include "mt_misc.h"
64 
65 
66 #define	RPC_MAX_BACKOFF		30 /* seconds */
67 
68 
69 static struct clnt_ops *clnt_dg_ops(void);
70 static bool_t time_not_ok(struct timeval *);
71 static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
72 	    xdrproc_t, void *, struct timeval);
73 static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
74 static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
75 static void clnt_dg_abort(CLIENT *);
76 static bool_t clnt_dg_control(CLIENT *, u_int, void *);
77 static void clnt_dg_destroy(CLIENT *);
78 
79 
80 
81 
82 /*
83  *	This machinery implements per-fd locks for MT-safety.  It is not
84  *	sufficient to do per-CLIENT handle locks for MT-safety because a
85  *	user may create more than one CLIENT handle with the same fd behind
86  *	it.  Therfore, we allocate an array of flags (dg_fd_locks), protected
87  *	by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
88  *	similarly protected.  Dg_fd_lock[fd] == 1 => a call is activte on some
89  *	CLIENT handle created for that fd.
90  *	The current implementation holds locks across the entire RPC and reply,
91  *	including retransmissions.  Yes, this is silly, and as soon as this
92  *	code is proven to work, this should be the first thing fixed.  One step
93  *	at a time.
94  */
95 static int	*dg_fd_locks;
96 static cond_t	*dg_cv;
97 #define	release_fd_lock(fd, mask) {		\
98 	mutex_lock(&clnt_fd_lock);	\
99 	dg_fd_locks[fd] = 0;		\
100 	mutex_unlock(&clnt_fd_lock);	\
101 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
102 	cond_signal(&dg_cv[fd]);	\
103 }
104 
105 static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
106 
107 /* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */
108 
109 /*
110  * Private data kept per client handle
111  */
112 struct cu_data {
113 	int			cu_fd;		/* connections fd */
114 	bool_t			cu_closeit;	/* opened by library */
115 	struct sockaddr_storage	cu_raddr;	/* remote address */
116 	int			cu_rlen;
117 	struct timeval		cu_wait;	/* retransmit interval */
118 	struct timeval		cu_total;	/* total time for the call */
119 	struct rpc_err		cu_error;
120 	XDR			cu_outxdrs;
121 	u_int			cu_xdrpos;
122 	u_int			cu_sendsz;	/* send size */
123 	char			*cu_outbuf;
124 	u_int			cu_recvsz;	/* recv size */
125 	int			cu_async;
126 	int			cu_connect;	/* Use connect(). */
127 	int			cu_connected;	/* Have done connect(). */
128 	struct kevent		cu_kin;
129 	int			cu_kq;
130 	char			cu_inbuf[1];
131 };
132 
133 /*
134  * Connection less client creation returns with client handle parameters.
135  * Default options are set, which the user can change using clnt_control().
136  * fd should be open and bound.
137  * NB: The rpch->cl_auth is initialized to null authentication.
138  * 	Caller may wish to set this something more useful.
139  *
140  * sendsz and recvsz are the maximum allowable packet sizes that can be
141  * sent and received. Normally they are the same, but they can be
142  * changed to improve the program efficiency and buffer allocation.
143  * If they are 0, use the transport default.
144  *
145  * If svcaddr is NULL, returns NULL.
146  */
147 CLIENT *
148 clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
149 	int fd;				/* open file descriptor */
150 	const struct netbuf *svcaddr;	/* servers address */
151 	rpcprog_t program;		/* program number */
152 	rpcvers_t version;		/* version number */
153 	u_int sendsz;			/* buffer recv size */
154 	u_int recvsz;			/* buffer send size */
155 {
156 	CLIENT *cl = NULL;		/* client handle */
157 	struct cu_data *cu = NULL;	/* private data */
158 	struct timeval now;
159 	struct rpc_msg call_msg;
160 	sigset_t mask;
161 	sigset_t newmask;
162 	struct __rpc_sockinfo si;
163 	int one = 1;
164 
165 	sigfillset(&newmask);
166 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
167 	mutex_lock(&clnt_fd_lock);
168 	if (dg_fd_locks == (int *) NULL) {
169 		int cv_allocsz;
170 		size_t fd_allocsz;
171 		int dtbsize = __rpc_dtbsize();
172 
173 		fd_allocsz = dtbsize * sizeof (int);
174 		dg_fd_locks = (int *) mem_alloc(fd_allocsz);
175 		if (dg_fd_locks == (int *) NULL) {
176 			mutex_unlock(&clnt_fd_lock);
177 			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
178 			goto err1;
179 		} else
180 			memset(dg_fd_locks, '\0', fd_allocsz);
181 
182 		cv_allocsz = dtbsize * sizeof (cond_t);
183 		dg_cv = (cond_t *) mem_alloc(cv_allocsz);
184 		if (dg_cv == (cond_t *) NULL) {
185 			mem_free(dg_fd_locks, fd_allocsz);
186 			dg_fd_locks = (int *) NULL;
187 			mutex_unlock(&clnt_fd_lock);
188 			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
189 			goto err1;
190 		} else {
191 			int i;
192 
193 			for (i = 0; i < dtbsize; i++)
194 				cond_init(&dg_cv[i], 0, (void *) 0);
195 		}
196 	}
197 
198 	mutex_unlock(&clnt_fd_lock);
199 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
200 
201 	if (svcaddr == NULL) {
202 		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
203 		return (NULL);
204 	}
205 
206 	if (!__rpc_fd2sockinfo(fd, &si)) {
207 		rpc_createerr.cf_stat = RPC_TLIERROR;
208 		rpc_createerr.cf_error.re_errno = 0;
209 		return (NULL);
210 	}
211 	/*
212 	 * Find the receive and the send size
213 	 */
214 	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
215 	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
216 	if ((sendsz == 0) || (recvsz == 0)) {
217 		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
218 		rpc_createerr.cf_error.re_errno = 0;
219 		return (NULL);
220 	}
221 
222 	if ((cl = mem_alloc(sizeof (CLIENT))) == NULL)
223 		goto err1;
224 	/*
225 	 * Should be multiple of 4 for XDR.
226 	 */
227 	sendsz = ((sendsz + 3) / 4) * 4;
228 	recvsz = ((recvsz + 3) / 4) * 4;
229 	cu = mem_alloc(sizeof (*cu) + sendsz + recvsz);
230 	if (cu == NULL)
231 		goto err1;
232 	(void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len);
233 	cu->cu_rlen = svcaddr->len;
234 	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
235 	/* Other values can also be set through clnt_control() */
236 	cu->cu_wait.tv_sec = 15;	/* heuristically chosen */
237 	cu->cu_wait.tv_usec = 0;
238 	cu->cu_total.tv_sec = -1;
239 	cu->cu_total.tv_usec = -1;
240 	cu->cu_sendsz = sendsz;
241 	cu->cu_recvsz = recvsz;
242 	cu->cu_async = FALSE;
243 	cu->cu_connect = FALSE;
244 	cu->cu_connected = FALSE;
245 	(void) gettimeofday(&now, NULL);
246 	call_msg.rm_xid = __RPC_GETXID(&now);
247 	call_msg.rm_call.cb_prog = program;
248 	call_msg.rm_call.cb_vers = version;
249 	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
250 	if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
251 		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
252 		rpc_createerr.cf_error.re_errno = 0;
253 		goto err2;
254 	}
255 	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
256 
257 	/* XXX fvdl - do we still want this? */
258 #if 0
259 	(void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf);
260 #endif
261 	_ioctl(fd, FIONBIO, (char *)(void *)&one);
262 
263 	/*
264 	 * By default, closeit is always FALSE. It is users responsibility
265 	 * to do a close on it, else the user may use clnt_control
266 	 * to let clnt_destroy do it for him/her.
267 	 */
268 	cu->cu_closeit = FALSE;
269 	cu->cu_fd = fd;
270 	cl->cl_ops = clnt_dg_ops();
271 	cl->cl_private = (caddr_t)(void *)cu;
272 	cl->cl_auth = authnone_create();
273 	cl->cl_tp = NULL;
274 	cl->cl_netid = NULL;
275 	cu->cu_kq = -1;
276 	EV_SET(&cu->cu_kin, cu->cu_fd, EVFILT_READ, EV_ADD, 0, 0, 0);
277 	return (cl);
278 err1:
279 	warnx(mem_err_clnt_dg);
280 	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
281 	rpc_createerr.cf_error.re_errno = errno;
282 err2:
283 	if (cl) {
284 		mem_free(cl, sizeof (CLIENT));
285 		if (cu)
286 			mem_free(cu, sizeof (*cu) + sendsz + recvsz);
287 	}
288 	return (NULL);
289 }
290 
291 static enum clnt_stat
292 clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
293 	CLIENT	*cl;			/* client handle */
294 	rpcproc_t	proc;		/* procedure number */
295 	xdrproc_t	xargs;		/* xdr routine for args */
296 	void		*argsp;		/* pointer to args */
297 	xdrproc_t	xresults;	/* xdr routine for results */
298 	void		*resultsp;	/* pointer to results */
299 	struct timeval	utimeout;	/* seconds to wait before giving up */
300 {
301 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
302 	XDR *xdrs;
303 	size_t outlen = 0;
304 	struct rpc_msg reply_msg;
305 	XDR reply_xdrs;
306 	bool_t ok;
307 	int nrefreshes = 2;		/* number of times to refresh cred */
308 	struct timeval timeout;
309 	struct timeval retransmit_time;
310 	struct timeval next_sendtime, starttime, time_waited, tv;
311 	struct timespec ts;
312 	struct kevent kv;
313 	struct sockaddr *sa;
314 	sigset_t mask;
315 	sigset_t newmask;
316 	socklen_t inlen, salen;
317 	ssize_t recvlen = 0;
318 	int kin_len, n, rpc_lock_value;
319 	u_int32_t xid;
320 
321 	outlen = 0;
322 	sigfillset(&newmask);
323 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
324 	mutex_lock(&clnt_fd_lock);
325 	while (dg_fd_locks[cu->cu_fd])
326 		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
327 	if (__isthreaded)
328 		rpc_lock_value = 1;
329 	else
330 		rpc_lock_value = 0;
331 	dg_fd_locks[cu->cu_fd] = rpc_lock_value;
332 	mutex_unlock(&clnt_fd_lock);
333 	if (cu->cu_total.tv_usec == -1) {
334 		timeout = utimeout;	/* use supplied timeout */
335 	} else {
336 		timeout = cu->cu_total;	/* use default timeout */
337 	}
338 
339 	if (cu->cu_connect && !cu->cu_connected) {
340 		if (_connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr,
341 		    cu->cu_rlen) < 0) {
342 			cu->cu_error.re_errno = errno;
343 			cu->cu_error.re_status = RPC_CANTSEND;
344 			goto out;
345 		}
346 		cu->cu_connected = 1;
347 	}
348 	if (cu->cu_connected) {
349 		sa = NULL;
350 		salen = 0;
351 	} else {
352 		sa = (struct sockaddr *)&cu->cu_raddr;
353 		salen = cu->cu_rlen;
354 	}
355 	time_waited.tv_sec = 0;
356 	time_waited.tv_usec = 0;
357 	retransmit_time = next_sendtime = cu->cu_wait;
358 	gettimeofday(&starttime, NULL);
359 
360 	/* Clean up in case the last call ended in a longjmp(3) call. */
361 	if (cu->cu_kq >= 0)
362 		_close(cu->cu_kq);
363 	if ((cu->cu_kq = kqueue()) < 0) {
364 		cu->cu_error.re_errno = errno;
365 		cu->cu_error.re_status = RPC_CANTSEND;
366 		goto out;
367 	}
368 	kin_len = 1;
369 
370 call_again:
371 	xdrs = &(cu->cu_outxdrs);
372 	if (cu->cu_async == TRUE && xargs == NULL)
373 		goto get_reply;
374 	xdrs->x_op = XDR_ENCODE;
375 	XDR_SETPOS(xdrs, cu->cu_xdrpos);
376 	/*
377 	 * the transaction is the first thing in the out buffer
378 	 * XXX Yes, and it's in network byte order, so we should to
379 	 * be careful when we increment it, shouldn't we.
380 	 */
381 	xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf));
382 	xid++;
383 	*(u_int32_t *)(void *)(cu->cu_outbuf) = htonl(xid);
384 
385 	if ((! XDR_PUTINT32(xdrs, &proc)) ||
386 	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
387 	    (! (*xargs)(xdrs, argsp))) {
388 		cu->cu_error.re_status = RPC_CANTENCODEARGS;
389 		goto out;
390 	}
391 	outlen = (size_t)XDR_GETPOS(xdrs);
392 
393 send_again:
394 	if (_sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) {
395 		cu->cu_error.re_errno = errno;
396 		cu->cu_error.re_status = RPC_CANTSEND;
397 		goto out;
398 	}
399 
400 	/*
401 	 * Hack to provide rpc-based message passing
402 	 */
403 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
404 		cu->cu_error.re_status = RPC_TIMEDOUT;
405 		goto out;
406 	}
407 
408 get_reply:
409 
410 	/*
411 	 * sub-optimal code appears here because we have
412 	 * some clock time to spare while the packets are in flight.
413 	 * (We assume that this is actually only executed once.)
414 	 */
415 	reply_msg.acpted_rply.ar_verf = _null_auth;
416 	reply_msg.acpted_rply.ar_results.where = resultsp;
417 	reply_msg.acpted_rply.ar_results.proc = xresults;
418 
419 	for (;;) {
420 		/* Decide how long to wait. */
421 		if (timercmp(&next_sendtime, &timeout, <))
422 			timersub(&next_sendtime, &time_waited, &tv);
423 		else
424 			timersub(&timeout, &time_waited, &tv);
425 		if (tv.tv_sec < 0 || tv.tv_usec < 0)
426 			tv.tv_sec = tv.tv_usec = 0;
427 		TIMEVAL_TO_TIMESPEC(&tv, &ts);
428 
429 		n = _kevent(cu->cu_kq, &cu->cu_kin, kin_len, &kv, 1, &ts);
430 		/* We don't need to register the event again. */
431 		kin_len = 0;
432 
433 		if (n == 1) {
434 			if (kv.flags & EV_ERROR) {
435 				cu->cu_error.re_errno = kv.data;
436 				cu->cu_error.re_status = RPC_CANTRECV;
437 				goto out;
438 			}
439 			/* We have some data now */
440 			do {
441 				recvlen = _recvfrom(cu->cu_fd, cu->cu_inbuf,
442 				    cu->cu_recvsz, 0, NULL, NULL);
443 			} while (recvlen < 0 && errno == EINTR);
444 			if (recvlen < 0 && errno != EWOULDBLOCK) {
445 				cu->cu_error.re_errno = errno;
446 				cu->cu_error.re_status = RPC_CANTRECV;
447 				goto out;
448 			}
449 			if (recvlen >= sizeof(u_int32_t) &&
450 			    (cu->cu_async == TRUE ||
451 			    *((u_int32_t *)(void *)(cu->cu_inbuf)) ==
452 			    *((u_int32_t *)(void *)(cu->cu_outbuf)))) {
453 				/* We now assume we have the proper reply. */
454 				break;
455 			}
456 		}
457 		if (n == -1 && errno != EINTR) {
458 			cu->cu_error.re_errno = errno;
459 			cu->cu_error.re_status = RPC_CANTRECV;
460 			goto out;
461 		}
462 		gettimeofday(&tv, NULL);
463 		timersub(&tv, &starttime, &time_waited);
464 
465 		/* Check for timeout. */
466 		if (timercmp(&time_waited, &timeout, >)) {
467 			cu->cu_error.re_status = RPC_TIMEDOUT;
468 			goto out;
469 		}
470 
471 		/* Retransmit if necessary. */
472 		if (timercmp(&time_waited, &next_sendtime, >)) {
473 			/* update retransmit_time */
474 			if (retransmit_time.tv_sec < RPC_MAX_BACKOFF)
475 				timeradd(&retransmit_time, &retransmit_time,
476 				    &retransmit_time);
477 			timeradd(&next_sendtime, &retransmit_time,
478 			    &next_sendtime);
479 			goto send_again;
480 		}
481 	}
482 	inlen = (socklen_t)recvlen;
483 
484 	/*
485 	 * now decode and validate the response
486 	 */
487 
488 	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
489 	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
490 	/* XDR_DESTROY(&reply_xdrs);	save a few cycles on noop destroy */
491 	if (ok) {
492 		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
493 			(reply_msg.acpted_rply.ar_stat == SUCCESS))
494 			cu->cu_error.re_status = RPC_SUCCESS;
495 		else
496 			_seterr_reply(&reply_msg, &(cu->cu_error));
497 
498 		if (cu->cu_error.re_status == RPC_SUCCESS) {
499 			if (! AUTH_VALIDATE(cl->cl_auth,
500 					    &reply_msg.acpted_rply.ar_verf)) {
501 				cu->cu_error.re_status = RPC_AUTHERROR;
502 				cu->cu_error.re_why = AUTH_INVALIDRESP;
503 			}
504 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
505 				xdrs->x_op = XDR_FREE;
506 				(void) xdr_opaque_auth(xdrs,
507 					&(reply_msg.acpted_rply.ar_verf));
508 			}
509 		}		/* end successful completion */
510 		/*
511 		 * If unsuccesful AND error is an authentication error
512 		 * then refresh credentials and try again, else break
513 		 */
514 		else if (cu->cu_error.re_status == RPC_AUTHERROR)
515 			/* maybe our credentials need to be refreshed ... */
516 			if (nrefreshes > 0 &&
517 			    AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
518 				nrefreshes--;
519 				goto call_again;
520 			}
521 		/* end of unsuccessful completion */
522 	}	/* end of valid reply message */
523 	else {
524 		cu->cu_error.re_status = RPC_CANTDECODERES;
525 
526 	}
527 out:
528 	if (cu->cu_kq >= 0)
529 		_close(cu->cu_kq);
530 	cu->cu_kq = -1;
531 	release_fd_lock(cu->cu_fd, mask);
532 	return (cu->cu_error.re_status);
533 }
534 
535 static void
536 clnt_dg_geterr(cl, errp)
537 	CLIENT *cl;
538 	struct rpc_err *errp;
539 {
540 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
541 
542 	*errp = cu->cu_error;
543 }
544 
545 static bool_t
546 clnt_dg_freeres(cl, xdr_res, res_ptr)
547 	CLIENT *cl;
548 	xdrproc_t xdr_res;
549 	void *res_ptr;
550 {
551 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
552 	XDR *xdrs = &(cu->cu_outxdrs);
553 	bool_t dummy;
554 	sigset_t mask;
555 	sigset_t newmask;
556 
557 	sigfillset(&newmask);
558 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
559 	mutex_lock(&clnt_fd_lock);
560 	while (dg_fd_locks[cu->cu_fd])
561 		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
562 	xdrs->x_op = XDR_FREE;
563 	dummy = (*xdr_res)(xdrs, res_ptr);
564 	mutex_unlock(&clnt_fd_lock);
565 	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
566 	cond_signal(&dg_cv[cu->cu_fd]);
567 	return (dummy);
568 }
569 
570 /*ARGSUSED*/
571 static void
572 clnt_dg_abort(h)
573 	CLIENT *h;
574 {
575 }
576 
577 static bool_t
578 clnt_dg_control(cl, request, info)
579 	CLIENT *cl;
580 	u_int request;
581 	void *info;
582 {
583 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
584 	struct netbuf *addr;
585 	sigset_t mask;
586 	sigset_t newmask;
587 	int rpc_lock_value;
588 
589 	sigfillset(&newmask);
590 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
591 	mutex_lock(&clnt_fd_lock);
592 	while (dg_fd_locks[cu->cu_fd])
593 		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
594 	if (__isthreaded)
595                 rpc_lock_value = 1;
596         else
597                 rpc_lock_value = 0;
598 	dg_fd_locks[cu->cu_fd] = rpc_lock_value;
599 	mutex_unlock(&clnt_fd_lock);
600 	switch (request) {
601 	case CLSET_FD_CLOSE:
602 		cu->cu_closeit = TRUE;
603 		release_fd_lock(cu->cu_fd, mask);
604 		return (TRUE);
605 	case CLSET_FD_NCLOSE:
606 		cu->cu_closeit = FALSE;
607 		release_fd_lock(cu->cu_fd, mask);
608 		return (TRUE);
609 	}
610 
611 	/* for other requests which use info */
612 	if (info == NULL) {
613 		release_fd_lock(cu->cu_fd, mask);
614 		return (FALSE);
615 	}
616 	switch (request) {
617 	case CLSET_TIMEOUT:
618 		if (time_not_ok((struct timeval *)info)) {
619 			release_fd_lock(cu->cu_fd, mask);
620 			return (FALSE);
621 		}
622 		cu->cu_total = *(struct timeval *)info;
623 		break;
624 	case CLGET_TIMEOUT:
625 		*(struct timeval *)info = cu->cu_total;
626 		break;
627 	case CLGET_SERVER_ADDR:		/* Give him the fd address */
628 		/* Now obsolete. Only for backward compatibility */
629 		(void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen);
630 		break;
631 	case CLSET_RETRY_TIMEOUT:
632 		if (time_not_ok((struct timeval *)info)) {
633 			release_fd_lock(cu->cu_fd, mask);
634 			return (FALSE);
635 		}
636 		cu->cu_wait = *(struct timeval *)info;
637 		break;
638 	case CLGET_RETRY_TIMEOUT:
639 		*(struct timeval *)info = cu->cu_wait;
640 		break;
641 	case CLGET_FD:
642 		*(int *)info = cu->cu_fd;
643 		break;
644 	case CLGET_SVC_ADDR:
645 		addr = (struct netbuf *)info;
646 		addr->buf = &cu->cu_raddr;
647 		addr->len = cu->cu_rlen;
648 		addr->maxlen = sizeof cu->cu_raddr;
649 		break;
650 	case CLSET_SVC_ADDR:		/* set to new address */
651 		addr = (struct netbuf *)info;
652 		if (addr->len < sizeof cu->cu_raddr) {
653 			release_fd_lock(cu->cu_fd, mask);
654 			return (FALSE);
655 		}
656 		(void) memcpy(&cu->cu_raddr, addr->buf, addr->len);
657 		cu->cu_rlen = addr->len;
658 		break;
659 	case CLGET_XID:
660 		/*
661 		 * use the knowledge that xid is the
662 		 * first element in the call structure *.
663 		 * This will get the xid of the PREVIOUS call
664 		 */
665 		*(u_int32_t *)info =
666 		    ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
667 		break;
668 
669 	case CLSET_XID:
670 		/* This will set the xid of the NEXT call */
671 		*(u_int32_t *)(void *)cu->cu_outbuf =
672 		    htonl(*(u_int32_t *)info - 1);
673 		/* decrement by 1 as clnt_dg_call() increments once */
674 		break;
675 
676 	case CLGET_VERS:
677 		/*
678 		 * This RELIES on the information that, in the call body,
679 		 * the version number field is the fifth field from the
680 		 * begining of the RPC header. MUST be changed if the
681 		 * call_struct is changed
682 		 */
683 		*(u_int32_t *)info =
684 		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
685 		    4 * BYTES_PER_XDR_UNIT));
686 		break;
687 
688 	case CLSET_VERS:
689 		*(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
690 			= htonl(*(u_int32_t *)info);
691 		break;
692 
693 	case CLGET_PROG:
694 		/*
695 		 * This RELIES on the information that, in the call body,
696 		 * the program number field is the fourth field from the
697 		 * begining of the RPC header. MUST be changed if the
698 		 * call_struct is changed
699 		 */
700 		*(u_int32_t *)info =
701 		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
702 		    3 * BYTES_PER_XDR_UNIT));
703 		break;
704 
705 	case CLSET_PROG:
706 		*(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
707 			= htonl(*(u_int32_t *)info);
708 		break;
709 	case CLSET_ASYNC:
710 		cu->cu_async = *(int *)info;
711 		break;
712 	case CLSET_CONNECT:
713 		cu->cu_connect = *(int *)info;
714 		break;
715 	default:
716 		release_fd_lock(cu->cu_fd, mask);
717 		return (FALSE);
718 	}
719 	release_fd_lock(cu->cu_fd, mask);
720 	return (TRUE);
721 }
722 
723 static void
724 clnt_dg_destroy(cl)
725 	CLIENT *cl;
726 {
727 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
728 	int cu_fd = cu->cu_fd;
729 	sigset_t mask;
730 	sigset_t newmask;
731 
732 	sigfillset(&newmask);
733 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
734 	mutex_lock(&clnt_fd_lock);
735 	while (dg_fd_locks[cu_fd])
736 		cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
737 	if (cu->cu_closeit)
738 		(void)_close(cu_fd);
739 	if (cu->cu_kq >= 0)
740 		_close(cu->cu_kq);
741 	XDR_DESTROY(&(cu->cu_outxdrs));
742 	mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
743 	if (cl->cl_netid && cl->cl_netid[0])
744 		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
745 	if (cl->cl_tp && cl->cl_tp[0])
746 		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
747 	mem_free(cl, sizeof (CLIENT));
748 	mutex_unlock(&clnt_fd_lock);
749 	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
750 	cond_signal(&dg_cv[cu_fd]);
751 }
752 
753 static struct clnt_ops *
754 clnt_dg_ops()
755 {
756 	static struct clnt_ops ops;
757 	sigset_t mask;
758 	sigset_t newmask;
759 
760 /* VARIABLES PROTECTED BY ops_lock: ops */
761 
762 	sigfillset(&newmask);
763 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
764 	mutex_lock(&ops_lock);
765 	if (ops.cl_call == NULL) {
766 		ops.cl_call = clnt_dg_call;
767 		ops.cl_abort = clnt_dg_abort;
768 		ops.cl_geterr = clnt_dg_geterr;
769 		ops.cl_freeres = clnt_dg_freeres;
770 		ops.cl_destroy = clnt_dg_destroy;
771 		ops.cl_control = clnt_dg_control;
772 	}
773 	mutex_unlock(&ops_lock);
774 	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
775 	return (&ops);
776 }
777 
778 /*
779  * Make sure that the time is not garbage.  -1 value is allowed.
780  */
781 static bool_t
782 time_not_ok(t)
783 	struct timeval *t;
784 {
785 	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
786 		t->tv_usec < -1 || t->tv_usec > 1000000);
787 }
788 
789