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