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