xref: /freebsd/lib/libc/rpc/clnt_vc.c (revision 830940567b49bb0c08dfaed40418999e76616909)
1 /*	$NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 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 #if defined(LIBC_SCCS) && !defined(lint)
33 static char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
34 static char *sccsid = "@(#)clnt_tcp.c	2.2 88/08/01 4.0 RPCSRC";
35 static char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro";
36 #endif
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 /*
41  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
42  *
43  * Copyright (C) 1984, Sun Microsystems, Inc.
44  *
45  * TCP based RPC supports 'batched calls'.
46  * A sequence of calls may be batched-up in a send buffer.  The rpc call
47  * return immediately to the client even though the call was not necessarily
48  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
49  * the rpc timeout value is zero (see clnt.h, rpc).
50  *
51  * Clients should NOT casually batch calls that in fact return results; that is,
52  * the server side should be aware that a call is batched and not produce any
53  * return message.  Batched calls that produce many result messages can
54  * deadlock (netlock) the client and the server....
55  *
56  * Now go hang yourself.
57  */
58 
59 #include "namespace.h"
60 #include "reentrant.h"
61 #include <sys/types.h>
62 #include <sys/poll.h>
63 #include <sys/syslog.h>
64 #include <sys/socket.h>
65 #include <sys/un.h>
66 #include <sys/uio.h>
67 
68 #include <arpa/inet.h>
69 #include <assert.h>
70 #include <err.h>
71 #include <errno.h>
72 #include <netdb.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <unistd.h>
77 #include <signal.h>
78 
79 #include <rpc/rpc.h>
80 #include <rpc/rpcsec_gss.h>
81 #include "un-namespace.h"
82 #include "rpc_com.h"
83 #include "mt_misc.h"
84 
85 #define MCALL_MSG_SIZE 24
86 
87 struct cmessage {
88         struct cmsghdr cmsg;
89         struct cmsgcred cmcred;
90 };
91 
92 static enum clnt_stat clnt_vc_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
93     xdrproc_t, void *, struct timeval);
94 static void clnt_vc_geterr(CLIENT *, struct rpc_err *);
95 static bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *);
96 static void clnt_vc_abort(CLIENT *);
97 static bool_t clnt_vc_control(CLIENT *, u_int, void *);
98 static void clnt_vc_destroy(CLIENT *);
99 static struct clnt_ops *clnt_vc_ops(void);
100 static bool_t time_not_ok(struct timeval *);
101 static int read_vc(void *, void *, int);
102 static int write_vc(void *, void *, int);
103 static int __msgwrite(int, void *, size_t);
104 static int __msgread(int, void *, size_t);
105 
106 struct ct_data {
107 	int		ct_fd;		/* connection's fd */
108 	bool_t		ct_closeit;	/* close it on destroy */
109 	struct timeval	ct_wait;	/* wait interval in milliseconds */
110 	bool_t          ct_waitset;	/* wait set by clnt_control? */
111 	struct netbuf	ct_addr;	/* remote addr */
112 	struct rpc_err	ct_error;
113 	union {
114 		char	ct_mcallc[MCALL_MSG_SIZE];	/* marshalled callmsg */
115 		u_int32_t ct_mcalli;
116 	} ct_u;
117 	u_int		ct_mpos;	/* pos after marshal */
118 	XDR		ct_xdrs;	/* XDR stream */
119 };
120 
121 /*
122  *      This machinery implements per-fd locks for MT-safety.  It is not
123  *      sufficient to do per-CLIENT handle locks for MT-safety because a
124  *      user may create more than one CLIENT handle with the same fd behind
125  *      it.  Therfore, we allocate an array of flags (vc_fd_locks), protected
126  *      by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
127  *      similarly protected.  Vc_fd_lock[fd] == 1 => a call is activte on some
128  *      CLIENT handle created for that fd.
129  *      The current implementation holds locks across the entire RPC and reply.
130  *      Yes, this is silly, and as soon as this code is proven to work, this
131  *      should be the first thing fixed.  One step at a time.
132  */
133 static int      *vc_fd_locks;
134 static cond_t   *vc_cv;
135 #define release_fd_lock(fd, mask) {	\
136 	mutex_lock(&clnt_fd_lock);	\
137 	vc_fd_locks[fd] = 0;		\
138 	mutex_unlock(&clnt_fd_lock);	\
139 	thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL);	\
140 	cond_signal(&vc_cv[fd]);	\
141 }
142 
143 static const char clnt_vc_errstr[] = "%s : %s";
144 static const char clnt_vc_str[] = "clnt_vc_create";
145 static const char clnt_read_vc_str[] = "read_vc";
146 static const char __no_mem_str[] = "out of memory";
147 
148 /*
149  * Create a client handle for a connection.
150  * Default options are set, which the user can change using clnt_control()'s.
151  * The rpc/vc package does buffering similar to stdio, so the client
152  * must pick send and receive buffer sizes, 0 => use the default.
153  * NB: fd is copied into a private area.
154  * NB: The rpch->cl_auth is set null authentication. Caller may wish to
155  * set this something more useful.
156  *
157  * fd should be an open socket
158  */
159 CLIENT *
160 clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
161 	int fd;				/* open file descriptor */
162 	const struct netbuf *raddr;	/* servers address */
163 	const rpcprog_t prog;			/* program number */
164 	const rpcvers_t vers;			/* version number */
165 	u_int sendsz;			/* buffer recv size */
166 	u_int recvsz;			/* buffer send size */
167 {
168 	CLIENT *cl;			/* client handle */
169 	struct ct_data *ct = NULL;	/* client handle */
170 	struct timeval now;
171 	struct rpc_msg call_msg;
172 	static u_int32_t disrupt;
173 	sigset_t mask;
174 	sigset_t newmask;
175 	struct sockaddr_storage ss;
176 	socklen_t slen;
177 	struct __rpc_sockinfo si;
178 
179 	if (disrupt == 0)
180 		disrupt = (u_int32_t)(long)raddr;
181 
182 	cl = (CLIENT *)mem_alloc(sizeof (*cl));
183 	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
184 	if ((cl == (CLIENT *)NULL) || (ct == (struct ct_data *)NULL)) {
185 		(void) syslog(LOG_ERR, clnt_vc_errstr,
186 		    clnt_vc_str, __no_mem_str);
187 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
188 		rpc_createerr.cf_error.re_errno = errno;
189 		goto err;
190 	}
191 	ct->ct_addr.buf = NULL;
192 	sigfillset(&newmask);
193 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
194 	mutex_lock(&clnt_fd_lock);
195 	if (vc_fd_locks == (int *) NULL) {
196 		int cv_allocsz, fd_allocsz;
197 		int dtbsize = __rpc_dtbsize();
198 
199 		fd_allocsz = dtbsize * sizeof (int);
200 		vc_fd_locks = (int *) mem_alloc(fd_allocsz);
201 		if (vc_fd_locks == (int *) NULL) {
202 			mutex_unlock(&clnt_fd_lock);
203 			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
204 			goto err;
205 		} else
206 			memset(vc_fd_locks, '\0', fd_allocsz);
207 
208 		assert(vc_cv == (cond_t *) NULL);
209 		cv_allocsz = dtbsize * sizeof (cond_t);
210 		vc_cv = (cond_t *) mem_alloc(cv_allocsz);
211 		if (vc_cv == (cond_t *) NULL) {
212 			mem_free(vc_fd_locks, fd_allocsz);
213 			vc_fd_locks = (int *) NULL;
214 			mutex_unlock(&clnt_fd_lock);
215 			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
216 			goto err;
217 		} else {
218 			int i;
219 
220 			for (i = 0; i < dtbsize; i++)
221 				cond_init(&vc_cv[i], 0, (void *) 0);
222 		}
223 	} else
224 		assert(vc_cv != (cond_t *) NULL);
225 
226 	/*
227 	 * XXX - fvdl connecting while holding a mutex?
228 	 */
229 	slen = sizeof ss;
230 	if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
231 		if (errno != ENOTCONN) {
232 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
233 			rpc_createerr.cf_error.re_errno = errno;
234 			mutex_unlock(&clnt_fd_lock);
235 			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
236 			goto err;
237 		}
238 		if (_connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){
239 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
240 			rpc_createerr.cf_error.re_errno = errno;
241 			mutex_unlock(&clnt_fd_lock);
242 			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
243 			goto err;
244 		}
245 	}
246 	mutex_unlock(&clnt_fd_lock);
247 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
248 	if (!__rpc_fd2sockinfo(fd, &si))
249 		goto err;
250 
251 	ct->ct_closeit = FALSE;
252 
253 	/*
254 	 * Set up private data struct
255 	 */
256 	ct->ct_fd = fd;
257 	ct->ct_wait.tv_usec = 0;
258 	ct->ct_waitset = FALSE;
259 	ct->ct_addr.buf = malloc(raddr->maxlen);
260 	if (ct->ct_addr.buf == NULL)
261 		goto err;
262 	memcpy(ct->ct_addr.buf, raddr->buf, raddr->len);
263 	ct->ct_addr.len = raddr->maxlen;
264 	ct->ct_addr.maxlen = raddr->maxlen;
265 
266 	/*
267 	 * Initialize call message
268 	 */
269 	(void)gettimeofday(&now, NULL);
270 	call_msg.rm_xid = ((u_int32_t)++disrupt) ^ __RPC_GETXID(&now);
271 	call_msg.rm_direction = CALL;
272 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
273 	call_msg.rm_call.cb_prog = (u_int32_t)prog;
274 	call_msg.rm_call.cb_vers = (u_int32_t)vers;
275 
276 	/*
277 	 * pre-serialize the static part of the call msg and stash it away
278 	 */
279 	xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
280 	    XDR_ENCODE);
281 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
282 		if (ct->ct_closeit) {
283 			(void)_close(fd);
284 		}
285 		goto err;
286 	}
287 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
288 	XDR_DESTROY(&(ct->ct_xdrs));
289 	assert(ct->ct_mpos + sizeof(uint32_t) <= MCALL_MSG_SIZE);
290 
291 	/*
292 	 * Create a client handle which uses xdrrec for serialization
293 	 * and authnone for authentication.
294 	 */
295 	cl->cl_ops = clnt_vc_ops();
296 	cl->cl_private = ct;
297 	cl->cl_auth = authnone_create();
298 	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
299 	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
300 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
301 	    cl->cl_private, read_vc, write_vc);
302 	return (cl);
303 
304 err:
305 	if (cl) {
306 		if (ct) {
307 			if (ct->ct_addr.len)
308 				mem_free(ct->ct_addr.buf, ct->ct_addr.len);
309 			mem_free(ct, sizeof (struct ct_data));
310 		}
311 		if (cl)
312 			mem_free(cl, sizeof (CLIENT));
313 	}
314 	return ((CLIENT *)NULL);
315 }
316 
317 static enum clnt_stat
318 clnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
319 	CLIENT *cl;
320 	rpcproc_t proc;
321 	xdrproc_t xdr_args;
322 	void *args_ptr;
323 	xdrproc_t xdr_results;
324 	void *results_ptr;
325 	struct timeval timeout;
326 {
327 	struct ct_data *ct = (struct ct_data *) cl->cl_private;
328 	XDR *xdrs = &(ct->ct_xdrs);
329 	struct rpc_msg reply_msg;
330 	u_int32_t x_id;
331 	u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli;    /* yuk */
332 	bool_t shipnow;
333 	int refreshes = 2;
334 	sigset_t mask, newmask;
335 	int rpc_lock_value;
336 	bool_t reply_stat;
337 
338 	assert(cl != NULL);
339 
340 	sigfillset(&newmask);
341 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
342 	mutex_lock(&clnt_fd_lock);
343 	while (vc_fd_locks[ct->ct_fd])
344 		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
345 	if (__isthreaded)
346                 rpc_lock_value = 1;
347         else
348                 rpc_lock_value = 0;
349 	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
350 	mutex_unlock(&clnt_fd_lock);
351 	if (!ct->ct_waitset) {
352 		/* If time is not within limits, we ignore it. */
353 		if (time_not_ok(&timeout) == FALSE)
354 			ct->ct_wait = timeout;
355 	}
356 
357 	shipnow =
358 	    (xdr_results == NULL && timeout.tv_sec == 0
359 	    && timeout.tv_usec == 0) ? FALSE : TRUE;
360 
361 call_again:
362 	xdrs->x_op = XDR_ENCODE;
363 	ct->ct_error.re_status = RPC_SUCCESS;
364 	x_id = ntohl(--(*msg_x_id));
365 
366 	if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
367 		if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
368 		    (! XDR_PUTINT32(xdrs, &proc)) ||
369 		    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
370 		    (! (*xdr_args)(xdrs, args_ptr))) {
371 			if (ct->ct_error.re_status == RPC_SUCCESS)
372 				ct->ct_error.re_status = RPC_CANTENCODEARGS;
373 			(void)xdrrec_endofrecord(xdrs, TRUE);
374 			release_fd_lock(ct->ct_fd, mask);
375 			return (ct->ct_error.re_status);
376 		}
377 	} else {
378 		*(uint32_t *) &ct->ct_u.ct_mcallc[ct->ct_mpos] = htonl(proc);
379 		if (! __rpc_gss_wrap(cl->cl_auth, ct->ct_u.ct_mcallc,
380 			ct->ct_mpos + sizeof(uint32_t),
381 			xdrs, xdr_args, args_ptr)) {
382 			if (ct->ct_error.re_status == RPC_SUCCESS)
383 				ct->ct_error.re_status = RPC_CANTENCODEARGS;
384 			(void)xdrrec_endofrecord(xdrs, TRUE);
385 			release_fd_lock(ct->ct_fd, mask);
386 			return (ct->ct_error.re_status);
387 		}
388 	}
389 	if (! xdrrec_endofrecord(xdrs, shipnow)) {
390 		release_fd_lock(ct->ct_fd, mask);
391 		return (ct->ct_error.re_status = RPC_CANTSEND);
392 	}
393 	if (! shipnow) {
394 		release_fd_lock(ct->ct_fd, mask);
395 		return (RPC_SUCCESS);
396 	}
397 	/*
398 	 * Hack to provide rpc-based message passing
399 	 */
400 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
401 		release_fd_lock(ct->ct_fd, mask);
402 		return(ct->ct_error.re_status = RPC_TIMEDOUT);
403 	}
404 
405 
406 	/*
407 	 * Keep receiving until we get a valid transaction id
408 	 */
409 	xdrs->x_op = XDR_DECODE;
410 	while (TRUE) {
411 		reply_msg.acpted_rply.ar_verf = _null_auth;
412 		reply_msg.acpted_rply.ar_results.where = NULL;
413 		reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
414 		if (! xdrrec_skiprecord(xdrs)) {
415 			release_fd_lock(ct->ct_fd, mask);
416 			return (ct->ct_error.re_status);
417 		}
418 		/* now decode and validate the response header */
419 		if (! xdr_replymsg(xdrs, &reply_msg)) {
420 			if (ct->ct_error.re_status == RPC_SUCCESS)
421 				continue;
422 			release_fd_lock(ct->ct_fd, mask);
423 			return (ct->ct_error.re_status);
424 		}
425 		if (reply_msg.rm_xid == x_id)
426 			break;
427 	}
428 
429 	/*
430 	 * process header
431 	 */
432 	_seterr_reply(&reply_msg, &(ct->ct_error));
433 	if (ct->ct_error.re_status == RPC_SUCCESS) {
434 		if (! AUTH_VALIDATE(cl->cl_auth,
435 		    &reply_msg.acpted_rply.ar_verf)) {
436 			ct->ct_error.re_status = RPC_AUTHERROR;
437 			ct->ct_error.re_why = AUTH_INVALIDRESP;
438 		} else {
439 			if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
440 				reply_stat = (*xdr_results)(xdrs, results_ptr);
441 			} else {
442 				reply_stat = __rpc_gss_unwrap(cl->cl_auth,
443 				    xdrs, xdr_results, results_ptr);
444 			}
445 			if (! reply_stat) {
446 				if (ct->ct_error.re_status == RPC_SUCCESS)
447 					ct->ct_error.re_status =
448 						RPC_CANTDECODERES;
449 			}
450 		}
451 		/* free verifier ... */
452 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
453 			xdrs->x_op = XDR_FREE;
454 			(void)xdr_opaque_auth(xdrs,
455 			    &(reply_msg.acpted_rply.ar_verf));
456 		}
457 	}  /* end successful completion */
458 	else {
459 		/* maybe our credentials need to be refreshed ... */
460 		if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg))
461 			goto call_again;
462 	}  /* end of unsuccessful completion */
463 	release_fd_lock(ct->ct_fd, mask);
464 	return (ct->ct_error.re_status);
465 }
466 
467 static void
468 clnt_vc_geterr(cl, errp)
469 	CLIENT *cl;
470 	struct rpc_err *errp;
471 {
472 	struct ct_data *ct;
473 
474 	assert(cl != NULL);
475 	assert(errp != NULL);
476 
477 	ct = (struct ct_data *) cl->cl_private;
478 	*errp = ct->ct_error;
479 }
480 
481 static bool_t
482 clnt_vc_freeres(cl, xdr_res, res_ptr)
483 	CLIENT *cl;
484 	xdrproc_t xdr_res;
485 	void *res_ptr;
486 {
487 	struct ct_data *ct;
488 	XDR *xdrs;
489 	bool_t dummy;
490 	sigset_t mask;
491 	sigset_t newmask;
492 
493 	assert(cl != NULL);
494 
495 	ct = (struct ct_data *)cl->cl_private;
496 	xdrs = &(ct->ct_xdrs);
497 
498 	sigfillset(&newmask);
499 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
500 	mutex_lock(&clnt_fd_lock);
501 	while (vc_fd_locks[ct->ct_fd])
502 		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
503 	xdrs->x_op = XDR_FREE;
504 	dummy = (*xdr_res)(xdrs, res_ptr);
505 	mutex_unlock(&clnt_fd_lock);
506 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
507 	cond_signal(&vc_cv[ct->ct_fd]);
508 
509 	return dummy;
510 }
511 
512 /*ARGSUSED*/
513 static void
514 clnt_vc_abort(cl)
515 	CLIENT *cl;
516 {
517 }
518 
519 static bool_t
520 clnt_vc_control(cl, request, info)
521 	CLIENT *cl;
522 	u_int request;
523 	void *info;
524 {
525 	struct ct_data *ct;
526 	void *infop = info;
527 	sigset_t mask;
528 	sigset_t newmask;
529 	int rpc_lock_value;
530 
531 	assert(cl != NULL);
532 
533 	ct = (struct ct_data *)cl->cl_private;
534 
535 	sigfillset(&newmask);
536 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
537 	mutex_lock(&clnt_fd_lock);
538 	while (vc_fd_locks[ct->ct_fd])
539 		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
540 	if (__isthreaded)
541                 rpc_lock_value = 1;
542         else
543                 rpc_lock_value = 0;
544 	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
545 	mutex_unlock(&clnt_fd_lock);
546 
547 	switch (request) {
548 	case CLSET_FD_CLOSE:
549 		ct->ct_closeit = TRUE;
550 		release_fd_lock(ct->ct_fd, mask);
551 		return (TRUE);
552 	case CLSET_FD_NCLOSE:
553 		ct->ct_closeit = FALSE;
554 		release_fd_lock(ct->ct_fd, mask);
555 		return (TRUE);
556 	default:
557 		break;
558 	}
559 
560 	/* for other requests which use info */
561 	if (info == NULL) {
562 		release_fd_lock(ct->ct_fd, mask);
563 		return (FALSE);
564 	}
565 	switch (request) {
566 	case CLSET_TIMEOUT:
567 		if (time_not_ok((struct timeval *)info)) {
568 			release_fd_lock(ct->ct_fd, mask);
569 			return (FALSE);
570 		}
571 		ct->ct_wait = *(struct timeval *)infop;
572 		ct->ct_waitset = TRUE;
573 		break;
574 	case CLGET_TIMEOUT:
575 		*(struct timeval *)infop = ct->ct_wait;
576 		break;
577 	case CLGET_SERVER_ADDR:
578 		(void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
579 		break;
580 	case CLGET_FD:
581 		*(int *)info = ct->ct_fd;
582 		break;
583 	case CLGET_SVC_ADDR:
584 		/* The caller should not free this memory area */
585 		*(struct netbuf *)info = ct->ct_addr;
586 		break;
587 	case CLSET_SVC_ADDR:		/* set to new address */
588 		release_fd_lock(ct->ct_fd, mask);
589 		return (FALSE);
590 	case CLGET_XID:
591 		/*
592 		 * use the knowledge that xid is the
593 		 * first element in the call structure
594 		 * This will get the xid of the PREVIOUS call
595 		 */
596 		*(u_int32_t *)info =
597 		    ntohl(*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli);
598 		break;
599 	case CLSET_XID:
600 		/* This will set the xid of the NEXT call */
601 		*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli =
602 		    htonl(*((u_int32_t *)info) + 1);
603 		/* increment by 1 as clnt_vc_call() decrements once */
604 		break;
605 	case CLGET_VERS:
606 		/*
607 		 * This RELIES on the information that, in the call body,
608 		 * the version number field is the fifth field from the
609 		 * begining of the RPC header. MUST be changed if the
610 		 * call_struct is changed
611 		 */
612 		*(u_int32_t *)info =
613 		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
614 		    4 * BYTES_PER_XDR_UNIT));
615 		break;
616 
617 	case CLSET_VERS:
618 		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
619 		    4 * BYTES_PER_XDR_UNIT) =
620 		    htonl(*(u_int32_t *)info);
621 		break;
622 
623 	case CLGET_PROG:
624 		/*
625 		 * This RELIES on the information that, in the call body,
626 		 * the program number field is the fourth field from the
627 		 * begining of the RPC header. MUST be changed if the
628 		 * call_struct is changed
629 		 */
630 		*(u_int32_t *)info =
631 		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
632 		    3 * BYTES_PER_XDR_UNIT));
633 		break;
634 
635 	case CLSET_PROG:
636 		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
637 		    3 * BYTES_PER_XDR_UNIT) =
638 		    htonl(*(u_int32_t *)info);
639 		break;
640 
641 	default:
642 		release_fd_lock(ct->ct_fd, mask);
643 		return (FALSE);
644 	}
645 	release_fd_lock(ct->ct_fd, mask);
646 	return (TRUE);
647 }
648 
649 
650 static void
651 clnt_vc_destroy(cl)
652 	CLIENT *cl;
653 {
654 	struct ct_data *ct = (struct ct_data *) cl->cl_private;
655 	int ct_fd = ct->ct_fd;
656 	sigset_t mask;
657 	sigset_t newmask;
658 
659 	assert(cl != NULL);
660 
661 	ct = (struct ct_data *) cl->cl_private;
662 
663 	sigfillset(&newmask);
664 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
665 	mutex_lock(&clnt_fd_lock);
666 	while (vc_fd_locks[ct_fd])
667 		cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
668 	if (ct->ct_closeit && ct->ct_fd != -1) {
669 		(void)_close(ct->ct_fd);
670 	}
671 	XDR_DESTROY(&(ct->ct_xdrs));
672 	if (ct->ct_addr.buf)
673 		free(ct->ct_addr.buf);
674 	mem_free(ct, sizeof(struct ct_data));
675 	mem_free(cl, sizeof(CLIENT));
676 	mutex_unlock(&clnt_fd_lock);
677 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
678 	cond_signal(&vc_cv[ct_fd]);
679 }
680 
681 /*
682  * Interface between xdr serializer and tcp connection.
683  * Behaves like the system calls, read & write, but keeps some error state
684  * around for the rpc level.
685  */
686 static int
687 read_vc(ctp, buf, len)
688 	void *ctp;
689 	void *buf;
690 	int len;
691 {
692 	struct sockaddr sa;
693 	socklen_t sal;
694 	struct ct_data *ct = (struct ct_data *)ctp;
695 	struct pollfd fd;
696 	int milliseconds = (int)((ct->ct_wait.tv_sec * 1000) +
697 	    (ct->ct_wait.tv_usec / 1000));
698 
699 	if (len == 0)
700 		return (0);
701 	fd.fd = ct->ct_fd;
702 	fd.events = POLLIN;
703 	for (;;) {
704 		switch (_poll(&fd, 1, milliseconds)) {
705 		case 0:
706 			ct->ct_error.re_status = RPC_TIMEDOUT;
707 			return (-1);
708 
709 		case -1:
710 			if (errno == EINTR)
711 				continue;
712 			ct->ct_error.re_status = RPC_CANTRECV;
713 			ct->ct_error.re_errno = errno;
714 			return (-1);
715 		}
716 		break;
717 	}
718 
719 	sal = sizeof(sa);
720 	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
721 	    (sa.sa_family == AF_LOCAL)) {
722 		len = __msgread(ct->ct_fd, buf, (size_t)len);
723 	} else {
724 		len = _read(ct->ct_fd, buf, (size_t)len);
725 	}
726 
727 	switch (len) {
728 	case 0:
729 		/* premature eof */
730 		ct->ct_error.re_errno = ECONNRESET;
731 		ct->ct_error.re_status = RPC_CANTRECV;
732 		len = -1;  /* it's really an error */
733 		break;
734 
735 	case -1:
736 		ct->ct_error.re_errno = errno;
737 		ct->ct_error.re_status = RPC_CANTRECV;
738 		break;
739 	}
740 	return (len);
741 }
742 
743 static int
744 write_vc(ctp, buf, len)
745 	void *ctp;
746 	void *buf;
747 	int len;
748 {
749 	struct sockaddr sa;
750 	socklen_t sal;
751 	struct ct_data *ct = (struct ct_data *)ctp;
752 	int i, cnt;
753 
754 	sal = sizeof(sa);
755 	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
756 	    (sa.sa_family == AF_LOCAL)) {
757 		for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
758 			if ((i = __msgwrite(ct->ct_fd, buf,
759 			     (size_t)cnt)) == -1) {
760 				ct->ct_error.re_errno = errno;
761 				ct->ct_error.re_status = RPC_CANTSEND;
762 				return (-1);
763 			}
764 		}
765 	} else {
766 		for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
767 			if ((i = _write(ct->ct_fd, buf, (size_t)cnt)) == -1) {
768 				ct->ct_error.re_errno = errno;
769 				ct->ct_error.re_status = RPC_CANTSEND;
770 				return (-1);
771 			}
772 		}
773 	}
774 	return (len);
775 }
776 
777 static struct clnt_ops *
778 clnt_vc_ops()
779 {
780 	static struct clnt_ops ops;
781 	sigset_t mask, newmask;
782 
783 	/* VARIABLES PROTECTED BY ops_lock: ops */
784 
785 	sigfillset(&newmask);
786 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
787 	mutex_lock(&ops_lock);
788 	if (ops.cl_call == NULL) {
789 		ops.cl_call = clnt_vc_call;
790 		ops.cl_abort = clnt_vc_abort;
791 		ops.cl_geterr = clnt_vc_geterr;
792 		ops.cl_freeres = clnt_vc_freeres;
793 		ops.cl_destroy = clnt_vc_destroy;
794 		ops.cl_control = clnt_vc_control;
795 	}
796 	mutex_unlock(&ops_lock);
797 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
798 	return (&ops);
799 }
800 
801 /*
802  * Make sure that the time is not garbage.   -1 value is disallowed.
803  * Note this is different from time_not_ok in clnt_dg.c
804  */
805 static bool_t
806 time_not_ok(t)
807 	struct timeval *t;
808 {
809 	return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
810 		t->tv_usec <= -1 || t->tv_usec > 1000000);
811 }
812 
813 static int
814 __msgread(sock, buf, cnt)
815 	int sock;
816 	void *buf;
817 	size_t cnt;
818 {
819 	struct iovec iov[1];
820 	struct msghdr msg;
821 	union {
822 		struct cmsghdr cmsg;
823 		char control[CMSG_SPACE(sizeof(struct cmsgcred))];
824 	} cm;
825 
826 	bzero((char *)&cm, sizeof(cm));
827 	iov[0].iov_base = buf;
828 	iov[0].iov_len = cnt;
829 
830 	msg.msg_iov = iov;
831 	msg.msg_iovlen = 1;
832 	msg.msg_name = NULL;
833 	msg.msg_namelen = 0;
834 	msg.msg_control = (caddr_t)&cm;
835 	msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
836 	msg.msg_flags = 0;
837 
838 	return(_recvmsg(sock, &msg, 0));
839 }
840 
841 static int
842 __msgwrite(sock, buf, cnt)
843 	int sock;
844 	void *buf;
845 	size_t cnt;
846 {
847 	struct iovec iov[1];
848 	struct msghdr msg;
849 	union {
850 		struct cmsghdr cmsg;
851 		char control[CMSG_SPACE(sizeof(struct cmsgcred))];
852 	} cm;
853 
854 	bzero((char *)&cm, sizeof(cm));
855 	iov[0].iov_base = buf;
856 	iov[0].iov_len = cnt;
857 
858 	cm.cmsg.cmsg_type = SCM_CREDS;
859 	cm.cmsg.cmsg_level = SOL_SOCKET;
860 	cm.cmsg.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
861 
862 	msg.msg_iov = iov;
863 	msg.msg_iovlen = 1;
864 	msg.msg_name = NULL;
865 	msg.msg_namelen = 0;
866 	msg.msg_control = (caddr_t)&cm;
867 	msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
868 	msg.msg_flags = 0;
869 
870 	return(_sendmsg(sock, &msg, 0));
871 }
872