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