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