clnt_vc.c revision 75094
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 75094 2001-04-02 21:41:44Z 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	if (__isthreaded)		\
136		vc_fd_locks[fd] = 0;	\
137	mutex_unlock(&clnt_fd_lock);	\
138	thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL);	\
139	cond_signal(&vc_cv[fd]);	\
140}
141
142static const char clnt_vc_errstr[] = "%s : %s";
143static const char clnt_vc_str[] = "clnt_vc_create";
144static const char clnt_read_vc_str[] = "read_vc";
145static const char __no_mem_str[] = "out of memory";
146
147/*
148 * Create a client handle for a connection.
149 * Default options are set, which the user can change using clnt_control()'s.
150 * The rpc/vc package does buffering similar to stdio, so the client
151 * must pick send and receive buffer sizes, 0 => use the default.
152 * NB: fd is copied into a private area.
153 * NB: The rpch->cl_auth is set null authentication. Caller may wish to
154 * set this something more useful.
155 *
156 * fd should be an open socket
157 */
158CLIENT *
159clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
160	int fd;				/* open file descriptor */
161	const struct netbuf *raddr;	/* servers address */
162	rpcprog_t prog;			/* program number */
163	rpcvers_t vers;			/* version number */
164	u_int sendsz;			/* buffer recv size */
165	u_int recvsz;			/* buffer send size */
166{
167	CLIENT *cl;			/* client handle */
168	struct ct_data *ct = NULL;	/* client handle */
169	struct timeval now;
170	struct rpc_msg call_msg;
171	static u_int32_t disrupt;
172	sigset_t mask;
173	sigset_t newmask;
174	struct sockaddr_storage ss;
175	socklen_t slen;
176	struct __rpc_sockinfo si;
177
178	if (disrupt == 0)
179		disrupt = (u_int32_t)(long)raddr;
180
181	cl = (CLIENT *)mem_alloc(sizeof (*cl));
182	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
183	if ((cl == (CLIENT *)NULL) || (ct == (struct ct_data *)NULL)) {
184		(void) syslog(LOG_ERR, clnt_vc_errstr,
185		    clnt_vc_str, __no_mem_str);
186		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
187		rpc_createerr.cf_error.re_errno = errno;
188		goto err;
189	}
190	ct->ct_addr.buf = NULL;
191	sigfillset(&newmask);
192	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
193	mutex_lock(&clnt_fd_lock);
194	if (vc_fd_locks == (int *) NULL) {
195		int cv_allocsz, fd_allocsz;
196		int dtbsize = __rpc_dtbsize();
197
198		fd_allocsz = dtbsize * sizeof (int);
199		vc_fd_locks = (int *) mem_alloc(fd_allocsz);
200		if (vc_fd_locks == (int *) NULL) {
201			mutex_unlock(&clnt_fd_lock);
202			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
203			goto err;
204		} else
205			memset(vc_fd_locks, '\0', fd_allocsz);
206
207		assert(vc_cv == (cond_t *) NULL);
208		cv_allocsz = dtbsize * sizeof (cond_t);
209		vc_cv = (cond_t *) mem_alloc(cv_allocsz);
210		if (vc_cv == (cond_t *) NULL) {
211			mem_free(vc_fd_locks, fd_allocsz);
212			vc_fd_locks = (int *) NULL;
213			mutex_unlock(&clnt_fd_lock);
214			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
215			goto err;
216		} else {
217			int i;
218
219			for (i = 0; i < dtbsize; i++)
220				cond_init(&vc_cv[i], 0, (void *) 0);
221		}
222	} else
223		assert(vc_cv != (cond_t *) NULL);
224
225	/*
226	 * XXX - fvdl connecting while holding a mutex?
227	 */
228	slen = sizeof ss;
229	if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
230		if (errno != ENOTCONN) {
231			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
232			rpc_createerr.cf_error.re_errno = errno;
233			mutex_unlock(&clnt_fd_lock);
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			goto err;
241		}
242	}
243	mutex_unlock(&clnt_fd_lock);
244	if (!__rpc_fd2sockinfo(fd, &si))
245		goto err;
246	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
247
248	ct->ct_closeit = FALSE;
249
250	/*
251	 * Set up private data struct
252	 */
253	ct->ct_fd = fd;
254	ct->ct_wait.tv_usec = 0;
255	ct->ct_waitset = FALSE;
256	ct->ct_addr.buf = malloc(raddr->maxlen);
257	if (ct->ct_addr.buf == NULL)
258		goto err;
259	memcpy(ct->ct_addr.buf, &raddr->buf, raddr->len);
260	ct->ct_addr.len = raddr->maxlen;
261	ct->ct_addr.maxlen = raddr->maxlen;
262
263	/*
264	 * Initialize call message
265	 */
266	(void)gettimeofday(&now, NULL);
267	call_msg.rm_xid = ((u_int32_t)++disrupt) ^ __RPC_GETXID(&now);
268	call_msg.rm_direction = CALL;
269	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
270	call_msg.rm_call.cb_prog = (u_int32_t)prog;
271	call_msg.rm_call.cb_vers = (u_int32_t)vers;
272
273	/*
274	 * pre-serialize the static part of the call msg and stash it away
275	 */
276	xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
277	    XDR_ENCODE);
278	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
279		if (ct->ct_closeit) {
280			(void)_close(fd);
281		}
282		goto err;
283	}
284	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
285	XDR_DESTROY(&(ct->ct_xdrs));
286
287	/*
288	 * Create a client handle which uses xdrrec for serialization
289	 * and authnone for authentication.
290	 */
291	cl->cl_ops = clnt_vc_ops();
292	cl->cl_private = ct;
293	cl->cl_auth = authnone_create();
294	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
295	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
296	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
297	    cl->cl_private, read_vc, write_vc);
298	return (cl);
299
300err:
301	if (cl) {
302		if (ct) {
303			if (ct->ct_addr.len)
304				mem_free(ct->ct_addr.buf, ct->ct_addr.len);
305			mem_free((caddr_t)ct, sizeof (struct ct_data));
306		}
307		if (cl)
308			mem_free((caddr_t)cl, sizeof (CLIENT));
309	}
310	return ((CLIENT *)NULL);
311}
312
313static enum clnt_stat
314clnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
315	CLIENT *cl;
316	rpcproc_t proc;
317	xdrproc_t xdr_args;
318	caddr_t args_ptr;
319	xdrproc_t xdr_results;
320	caddr_t results_ptr;
321	struct timeval timeout;
322{
323	struct ct_data *ct = (struct ct_data *) cl->cl_private;
324	XDR *xdrs = &(ct->ct_xdrs);
325	struct rpc_msg reply_msg;
326	u_int32_t x_id;
327	u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli;    /* yuk */
328	bool_t shipnow;
329	int refreshes = 2;
330	sigset_t mask, newmask;
331	int rpc_lock_value;
332
333	assert(cl != NULL);
334
335	sigfillset(&newmask);
336	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
337	mutex_lock(&clnt_fd_lock);
338	while (vc_fd_locks[ct->ct_fd])
339		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
340	if (__isthreaded)
341                rpc_lock_value = 1;
342        else
343                rpc_lock_value = 0;
344	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
345	mutex_unlock(&clnt_fd_lock);
346	if (!ct->ct_waitset) {
347		/* If time is not within limits, we ignore it. */
348		if (time_not_ok(&timeout) == FALSE)
349			ct->ct_wait = timeout;
350	}
351
352	shipnow =
353	    (xdr_results == NULL && timeout.tv_sec == 0
354	    && timeout.tv_usec == 0) ? FALSE : TRUE;
355
356call_again:
357	xdrs->x_op = XDR_ENCODE;
358	ct->ct_error.re_status = RPC_SUCCESS;
359	x_id = ntohl(--(*msg_x_id));
360
361	if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
362	    (! XDR_PUTINT32(xdrs, &proc)) ||
363	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
364	    (! (*xdr_args)(xdrs, args_ptr))) {
365		if (ct->ct_error.re_status == RPC_SUCCESS)
366			ct->ct_error.re_status = RPC_CANTENCODEARGS;
367		(void)xdrrec_endofrecord(xdrs, TRUE);
368		release_fd_lock(ct->ct_fd, mask);
369		return (ct->ct_error.re_status);
370	}
371	if (! xdrrec_endofrecord(xdrs, shipnow)) {
372		release_fd_lock(ct->ct_fd, mask);
373		return (ct->ct_error.re_status = RPC_CANTSEND);
374	}
375	if (! shipnow) {
376		release_fd_lock(ct->ct_fd, mask);
377		return (RPC_SUCCESS);
378	}
379	/*
380	 * Hack to provide rpc-based message passing
381	 */
382	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
383		release_fd_lock(ct->ct_fd, mask);
384		return(ct->ct_error.re_status = RPC_TIMEDOUT);
385	}
386
387
388	/*
389	 * Keep receiving until we get a valid transaction id
390	 */
391	xdrs->x_op = XDR_DECODE;
392	while (TRUE) {
393		reply_msg.acpted_rply.ar_verf = _null_auth;
394		reply_msg.acpted_rply.ar_results.where = NULL;
395		reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
396		if (! xdrrec_skiprecord(xdrs)) {
397			release_fd_lock(ct->ct_fd, mask);
398			return (ct->ct_error.re_status);
399		}
400		/* now decode and validate the response header */
401		if (! xdr_replymsg(xdrs, &reply_msg)) {
402			if (ct->ct_error.re_status == RPC_SUCCESS)
403				continue;
404			release_fd_lock(ct->ct_fd, mask);
405			return (ct->ct_error.re_status);
406		}
407		if (reply_msg.rm_xid == x_id)
408			break;
409	}
410
411	/*
412	 * process header
413	 */
414	_seterr_reply(&reply_msg, &(ct->ct_error));
415	if (ct->ct_error.re_status == RPC_SUCCESS) {
416		if (! AUTH_VALIDATE(cl->cl_auth,
417		    &reply_msg.acpted_rply.ar_verf)) {
418			ct->ct_error.re_status = RPC_AUTHERROR;
419			ct->ct_error.re_why = AUTH_INVALIDRESP;
420		} else if (! (*xdr_results)(xdrs, results_ptr)) {
421			if (ct->ct_error.re_status == RPC_SUCCESS)
422				ct->ct_error.re_status = RPC_CANTDECODERES;
423		}
424		/* free verifier ... */
425		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
426			xdrs->x_op = XDR_FREE;
427			(void)xdr_opaque_auth(xdrs,
428			    &(reply_msg.acpted_rply.ar_verf));
429		}
430	}  /* end successful completion */
431	else {
432		/* maybe our credentials need to be refreshed ... */
433		if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg))
434			goto call_again;
435	}  /* end of unsuccessful completion */
436	release_fd_lock(ct->ct_fd, mask);
437	return (ct->ct_error.re_status);
438}
439
440static void
441clnt_vc_geterr(cl, errp)
442	CLIENT *cl;
443	struct rpc_err *errp;
444{
445	struct ct_data *ct;
446
447	assert(cl != NULL);
448	assert(errp != NULL);
449
450	ct = (struct ct_data *) cl->cl_private;
451	*errp = ct->ct_error;
452}
453
454static bool_t
455clnt_vc_freeres(cl, xdr_res, res_ptr)
456	CLIENT *cl;
457	xdrproc_t xdr_res;
458	caddr_t res_ptr;
459{
460	struct ct_data *ct;
461	XDR *xdrs;
462	bool_t dummy;
463	sigset_t mask;
464	sigset_t newmask;
465
466	assert(cl != NULL);
467
468	ct = (struct ct_data *)cl->cl_private;
469	xdrs = &(ct->ct_xdrs);
470
471	sigfillset(&newmask);
472	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
473	mutex_lock(&clnt_fd_lock);
474	while (vc_fd_locks[ct->ct_fd])
475		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
476	xdrs->x_op = XDR_FREE;
477	dummy = (*xdr_res)(xdrs, res_ptr);
478	mutex_unlock(&clnt_fd_lock);
479	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
480	cond_signal(&vc_cv[ct->ct_fd]);
481
482	return dummy;
483}
484
485/*ARGSUSED*/
486static void
487clnt_vc_abort(cl)
488	CLIENT *cl;
489{
490}
491
492static bool_t
493clnt_vc_control(cl, request, info)
494	CLIENT *cl;
495	u_int request;
496	char *info;
497{
498	struct ct_data *ct;
499	void *infop = info;
500	sigset_t mask;
501	sigset_t newmask;
502	int rpc_lock_value;
503
504	assert(cl != NULL);
505
506	ct = (struct ct_data *)cl->cl_private;
507
508	sigfillset(&newmask);
509	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
510	mutex_lock(&clnt_fd_lock);
511	while (vc_fd_locks[ct->ct_fd])
512		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
513	if (__isthreaded)
514                rpc_lock_value = 1;
515        else
516                rpc_lock_value = 0;
517	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
518	mutex_unlock(&clnt_fd_lock);
519
520	switch (request) {
521	case CLSET_FD_CLOSE:
522		ct->ct_closeit = TRUE;
523		release_fd_lock(ct->ct_fd, mask);
524		return (TRUE);
525	case CLSET_FD_NCLOSE:
526		ct->ct_closeit = FALSE;
527		release_fd_lock(ct->ct_fd, mask);
528		return (TRUE);
529	default:
530		break;
531	}
532
533	/* for other requests which use info */
534	if (info == NULL) {
535		release_fd_lock(ct->ct_fd, mask);
536		return (FALSE);
537	}
538	switch (request) {
539	case CLSET_TIMEOUT:
540		if (time_not_ok((struct timeval *)(void *)info)) {
541			release_fd_lock(ct->ct_fd, mask);
542			return (FALSE);
543		}
544		ct->ct_wait = *(struct timeval *)infop;
545		ct->ct_waitset = TRUE;
546		break;
547	case CLGET_TIMEOUT:
548		*(struct timeval *)infop = ct->ct_wait;
549		break;
550	case CLGET_SERVER_ADDR:
551		(void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
552		break;
553	case CLGET_FD:
554		*(int *)(void *)info = ct->ct_fd;
555		break;
556	case CLGET_SVC_ADDR:
557		/* The caller should not free this memory area */
558		*(struct netbuf *)(void *)info = ct->ct_addr;
559		break;
560	case CLSET_SVC_ADDR:		/* set to new address */
561		release_fd_lock(ct->ct_fd, mask);
562		return (FALSE);
563	case CLGET_XID:
564		/*
565		 * use the knowledge that xid is the
566		 * first element in the call structure
567		 * This will get the xid of the PREVIOUS call
568		 */
569		*(u_int32_t *)(void *)info =
570		    ntohl(*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli);
571		break;
572	case CLSET_XID:
573		/* This will set the xid of the NEXT call */
574		*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli =
575		    htonl(*((u_int32_t *)(void *)info) + 1);
576		/* increment by 1 as clnt_vc_call() decrements once */
577		break;
578	case CLGET_VERS:
579		/*
580		 * This RELIES on the information that, in the call body,
581		 * the version number field is the fifth field from the
582		 * begining of the RPC header. MUST be changed if the
583		 * call_struct is changed
584		 */
585		*(u_int32_t *)(void *)info =
586		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
587		    4 * BYTES_PER_XDR_UNIT));
588		break;
589
590	case CLSET_VERS:
591		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
592		    4 * BYTES_PER_XDR_UNIT) =
593		    htonl(*(u_int32_t *)(void *)info);
594		break;
595
596	case CLGET_PROG:
597		/*
598		 * This RELIES on the information that, in the call body,
599		 * the program number field is the fourth field from the
600		 * begining of the RPC header. MUST be changed if the
601		 * call_struct is changed
602		 */
603		*(u_int32_t *)(void *)info =
604		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
605		    3 * BYTES_PER_XDR_UNIT));
606		break;
607
608	case CLSET_PROG:
609		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
610		    3 * BYTES_PER_XDR_UNIT) =
611		    htonl(*(u_int32_t *)(void *)info);
612		break;
613
614	default:
615		release_fd_lock(ct->ct_fd, mask);
616		return (FALSE);
617	}
618	release_fd_lock(ct->ct_fd, mask);
619	return (TRUE);
620}
621
622
623static void
624clnt_vc_destroy(cl)
625	CLIENT *cl;
626{
627	struct ct_data *ct = (struct ct_data *) cl->cl_private;
628	int ct_fd = ct->ct_fd;
629	sigset_t mask;
630	sigset_t newmask;
631
632	assert(cl != NULL);
633
634	ct = (struct ct_data *) cl->cl_private;
635
636	sigfillset(&newmask);
637	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
638	mutex_lock(&clnt_fd_lock);
639	while (vc_fd_locks[ct_fd])
640		cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
641	if (ct->ct_closeit && ct->ct_fd != -1) {
642		(void)_close(ct->ct_fd);
643	}
644	XDR_DESTROY(&(ct->ct_xdrs));
645	if (ct->ct_addr.buf)
646		free(ct->ct_addr.buf);
647	mem_free(ct, sizeof(struct ct_data));
648	mem_free(cl, sizeof(CLIENT));
649	mutex_unlock(&clnt_fd_lock);
650	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
651	cond_signal(&vc_cv[ct_fd]);
652}
653
654/*
655 * Interface between xdr serializer and tcp connection.
656 * Behaves like the system calls, read & write, but keeps some error state
657 * around for the rpc level.
658 */
659static int
660read_vc(ctp, buf, len)
661	caddr_t ctp;
662	caddr_t buf;
663	int len;
664{
665	struct sockaddr sa;
666	socklen_t sal;
667	struct ct_data *ct = (struct ct_data *)(void *)ctp;
668	struct pollfd fd;
669	int milliseconds = (int)((ct->ct_wait.tv_sec * 1000) +
670	    (ct->ct_wait.tv_usec / 1000));
671
672	if (len == 0)
673		return (0);
674	fd.fd = ct->ct_fd;
675	fd.events = POLLIN;
676	for (;;) {
677		switch (_poll(&fd, 1, milliseconds)) {
678		case 0:
679			ct->ct_error.re_status = RPC_TIMEDOUT;
680			return (-1);
681
682		case -1:
683			if (errno == EINTR)
684				continue;
685			ct->ct_error.re_status = RPC_CANTRECV;
686			ct->ct_error.re_errno = errno;
687			return (-1);
688		}
689		break;
690	}
691
692	sal = sizeof(sa);
693	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
694	    (sa.sa_family == AF_LOCAL)) {
695		len = __msgread(ct->ct_fd, buf, (size_t)len);
696	} else {
697		len = _read(ct->ct_fd, buf, (size_t)len);
698	}
699
700	switch (len) {
701	case 0:
702		/* premature eof */
703		ct->ct_error.re_errno = ECONNRESET;
704		ct->ct_error.re_status = RPC_CANTRECV;
705		len = -1;  /* it's really an error */
706		break;
707
708	case -1:
709		ct->ct_error.re_errno = errno;
710		ct->ct_error.re_status = RPC_CANTRECV;
711		break;
712	}
713	return (len);
714}
715
716static int
717write_vc(ctp, buf, len)
718	caddr_t ctp;
719	caddr_t buf;
720	int len;
721{
722	struct sockaddr sa;
723	socklen_t sal;
724	struct ct_data *ct = (struct ct_data *)(void *)ctp;
725	int i, cnt;
726
727	sal = sizeof(sa);
728	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
729	    (sa.sa_family == AF_LOCAL)) {
730		for (cnt = len; cnt > 0; cnt -= i, buf += i) {
731			if ((i = __msgwrite(ct->ct_fd, buf,
732			     (size_t)cnt)) == -1) {
733				ct->ct_error.re_errno = errno;
734				ct->ct_error.re_status = RPC_CANTSEND;
735				return (-1);
736			}
737		}
738	} else {
739		for (cnt = len; cnt > 0; cnt -= i, buf += i) {
740			if ((i = _write(ct->ct_fd, buf, (size_t)cnt)) == -1) {
741				ct->ct_error.re_errno = errno;
742				ct->ct_error.re_status = RPC_CANTSEND;
743				return (-1);
744			}
745		}
746	}
747	return (len);
748}
749
750static struct clnt_ops *
751clnt_vc_ops()
752{
753	static struct clnt_ops ops;
754	extern mutex_t  ops_lock;
755	sigset_t mask, newmask;
756
757	/* VARIABLES PROTECTED BY ops_lock: ops */
758
759	sigfillset(&newmask);
760	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
761	mutex_lock(&ops_lock);
762	if (ops.cl_call == NULL) {
763		ops.cl_call = clnt_vc_call;
764		ops.cl_abort = clnt_vc_abort;
765		ops.cl_geterr = clnt_vc_geterr;
766		ops.cl_freeres = clnt_vc_freeres;
767		ops.cl_destroy = clnt_vc_destroy;
768		ops.cl_control = clnt_vc_control;
769	}
770	mutex_unlock(&ops_lock);
771	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
772	return (&ops);
773}
774
775/*
776 * Make sure that the time is not garbage.   -1 value is disallowed.
777 * Note this is different from time_not_ok in clnt_dg.c
778 */
779static bool_t
780time_not_ok(t)
781	struct timeval *t;
782{
783	return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
784		t->tv_usec <= -1 || t->tv_usec > 1000000);
785}
786
787static int
788__msgread(sock, buf, cnt)
789	int sock;
790	void *buf;
791	size_t cnt;
792{
793	struct iovec iov[1];
794	struct msghdr msg;
795	struct cmessage cm;
796
797	bzero((char *)&cm, sizeof(cm));
798	iov[0].iov_base = buf;
799	iov[0].iov_len = cnt;
800
801	msg.msg_iov = iov;
802	msg.msg_iovlen = 1;
803	msg.msg_name = NULL;
804	msg.msg_namelen = 0;
805	msg.msg_control = (caddr_t)&cm;
806	msg.msg_controllen = sizeof(struct cmessage);
807	msg.msg_flags = 0;
808
809	return(_recvmsg(sock, &msg, 0));
810}
811
812static int
813__msgwrite(sock, buf, cnt)
814	int sock;
815	void *buf;
816	size_t cnt;
817{
818	struct iovec iov[1];
819	struct msghdr msg;
820	struct cmessage cm;
821
822	bzero((char *)&cm, sizeof(cm));
823	iov[0].iov_base = buf;
824	iov[0].iov_len = cnt;
825
826	cm.cmsg.cmsg_type = SCM_CREDS;
827	cm.cmsg.cmsg_level = SOL_SOCKET;
828	cm.cmsg.cmsg_len = sizeof(struct cmessage);
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 = sizeof(struct cmessage);
836	msg.msg_flags = 0;
837
838	return(_sendmsg(sock, &msg, 0));
839}
840