clnt_vc.c revision 266245
1243791Sdim/*	$NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $	*/
2243791Sdim
3243791Sdim/*-
4243791Sdim * Copyright (c) 2009, Sun Microsystems, Inc.
5243791Sdim * All rights reserved.
6243791Sdim *
7243791Sdim * Redistribution and use in source and binary forms, with or without
8243791Sdim * modification, are permitted provided that the following conditions are met:
9243791Sdim * - Redistributions of source code must retain the above copyright notice,
10243791Sdim *   this list of conditions and the following disclaimer.
11243791Sdim * - Redistributions in binary form must reproduce the above copyright notice,
12243791Sdim *   this list of conditions and the following disclaimer in the documentation
13243791Sdim *   and/or other materials provided with the distribution.
14243791Sdim * - Neither the name of Sun Microsystems, Inc. nor the names of its
15243791Sdim *   contributors may be used to endorse or promote products derived
16243791Sdim *   from this software without specific prior written permission.
17252723Sdim *
18243791Sdim * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19252723Sdim * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20252723Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21252723Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22243791Sdim * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23243791Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24252723Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25252723Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26252723Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27252723Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28252723Sdim * POSSIBILITY OF SUCH DAMAGE.
29243791Sdim */
30243791Sdim
31243791Sdim#if defined(LIBC_SCCS) && !defined(lint)
32243791Sdimstatic char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
33243791Sdimstatic char *sccsid = "@(#)clnt_tcp.c	2.2 88/08/01 4.0 RPCSRC";
34243791Sdimstatic char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro";
35243791Sdim#endif
36243791Sdim#include <sys/cdefs.h>
37243791Sdim__FBSDID("$FreeBSD: stable/9/lib/libc/rpc/clnt_vc.c 266245 2014-05-16 15:51:37Z brueffer $");
38243791Sdim
39243791Sdim/*
40243791Sdim * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
41243791Sdim *
42243791Sdim * Copyright (C) 1984, Sun Microsystems, Inc.
43243791Sdim *
44243791Sdim * TCP based RPC supports 'batched calls'.
45243791Sdim * A sequence of calls may be batched-up in a send buffer.  The rpc call
46243791Sdim * return immediately to the client even though the call was not necessarily
47243791Sdim * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
48243791Sdim * the rpc timeout value is zero (see clnt.h, rpc).
49243791Sdim *
50243791Sdim * Clients should NOT casually batch calls that in fact return results; that is,
51243791Sdim * the server side should be aware that a call is batched and not produce any
52243791Sdim * return message.  Batched calls that produce many result messages can
53243791Sdim * deadlock (netlock) the client and the server....
54243791Sdim *
55243791Sdim * Now go hang yourself.
56243791Sdim */
57243791Sdim
58243791Sdim#include "namespace.h"
59243791Sdim#include "reentrant.h"
60243791Sdim#include <sys/types.h>
61243791Sdim#include <sys/poll.h>
62243791Sdim#include <sys/syslog.h>
63243791Sdim#include <sys/socket.h>
64243791Sdim#include <sys/un.h>
65243791Sdim#include <sys/uio.h>
66243791Sdim
67243791Sdim#include <arpa/inet.h>
68243791Sdim#include <assert.h>
69243791Sdim#include <err.h>
70243791Sdim#include <errno.h>
71243791Sdim#include <netdb.h>
72243791Sdim#include <stdio.h>
73243791Sdim#include <stdlib.h>
74243791Sdim#include <string.h>
75243791Sdim#include <unistd.h>
76243791Sdim#include <signal.h>
77243791Sdim
78243791Sdim#include <rpc/rpc.h>
79243791Sdim#include <rpc/rpcsec_gss.h>
80243791Sdim#include "un-namespace.h"
81243791Sdim#include "rpc_com.h"
82243791Sdim#include "mt_misc.h"
83243791Sdim
84243791Sdim#define MCALL_MSG_SIZE 24
85243791Sdim
86243791Sdimstruct cmessage {
87243791Sdim        struct cmsghdr cmsg;
88243791Sdim        struct cmsgcred cmcred;
89243791Sdim};
90243791Sdim
91243791Sdimstatic enum clnt_stat clnt_vc_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
92243791Sdim    xdrproc_t, void *, struct timeval);
93243791Sdimstatic void clnt_vc_geterr(CLIENT *, struct rpc_err *);
94243791Sdimstatic bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *);
95243791Sdimstatic void clnt_vc_abort(CLIENT *);
96243791Sdimstatic bool_t clnt_vc_control(CLIENT *, u_int, void *);
97243791Sdimstatic void clnt_vc_destroy(CLIENT *);
98243791Sdimstatic struct clnt_ops *clnt_vc_ops(void);
99243791Sdimstatic bool_t time_not_ok(struct timeval *);
100243791Sdimstatic int read_vc(void *, void *, int);
101243791Sdimstatic int write_vc(void *, void *, int);
102243791Sdimstatic int __msgwrite(int, void *, size_t);
103263509Sdimstatic int __msgread(int, void *, size_t);
104243791Sdim
105243791Sdimstruct ct_data {
106243791Sdim	int		ct_fd;		/* connection's fd */
107243791Sdim	bool_t		ct_closeit;	/* close it on destroy */
108243791Sdim	struct timeval	ct_wait;	/* wait interval in milliseconds */
109243791Sdim	bool_t          ct_waitset;	/* wait set by clnt_control? */
110243791Sdim	struct netbuf	ct_addr;	/* remote addr */
111243791Sdim	struct rpc_err	ct_error;
112243791Sdim	union {
113243791Sdim		char	ct_mcallc[MCALL_MSG_SIZE];	/* marshalled callmsg */
114243791Sdim		u_int32_t ct_mcalli;
115243791Sdim	} ct_u;
116243791Sdim	u_int		ct_mpos;	/* pos after marshal */
117243791Sdim	XDR		ct_xdrs;	/* XDR stream */
118243791Sdim};
119243791Sdim
120243791Sdim/*
121243791Sdim *      This machinery implements per-fd locks for MT-safety.  It is not
122243791Sdim *      sufficient to do per-CLIENT handle locks for MT-safety because a
123243791Sdim *      user may create more than one CLIENT handle with the same fd behind
124243791Sdim *      it.  Therfore, we allocate an array of flags (vc_fd_locks), protected
125243791Sdim *      by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
126243791Sdim *      similarly protected.  Vc_fd_lock[fd] == 1 => a call is activte on some
127243791Sdim *      CLIENT handle created for that fd.
128243791Sdim *      The current implementation holds locks across the entire RPC and reply.
129243791Sdim *      Yes, this is silly, and as soon as this code is proven to work, this
130243791Sdim *      should be the first thing fixed.  One step at a time.
131243791Sdim */
132243791Sdimstatic int      *vc_fd_locks;
133243791Sdimstatic cond_t   *vc_cv;
134243791Sdim#define release_fd_lock(fd, mask) {	\
135243791Sdim	mutex_lock(&clnt_fd_lock);	\
136243791Sdim	vc_fd_locks[fd] = 0;		\
137243791Sdim	mutex_unlock(&clnt_fd_lock);	\
138243791Sdim	thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL);	\
139243791Sdim	cond_signal(&vc_cv[fd]);	\
140243791Sdim}
141243791Sdim
142243791Sdimstatic const char clnt_vc_errstr[] = "%s : %s";
143243791Sdimstatic const char clnt_vc_str[] = "clnt_vc_create";
144243791Sdimstatic const char clnt_read_vc_str[] = "read_vc";
145243791Sdimstatic const char __no_mem_str[] = "out of memory";
146243791Sdim
147243791Sdim/*
148243791Sdim * Create a client handle for a connection.
149243791Sdim * Default options are set, which the user can change using clnt_control()'s.
150243791Sdim * The rpc/vc package does buffering similar to stdio, so the client
151243791Sdim * must pick send and receive buffer sizes, 0 => use the default.
152243791Sdim * NB: fd is copied into a private area.
153243791Sdim * NB: The rpch->cl_auth is set null authentication. Caller may wish to
154243791Sdim * set this something more useful.
155243791Sdim *
156243791Sdim * fd should be an open socket
157243791Sdim */
158243791SdimCLIENT *
159243791Sdimclnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
160243791Sdim	int fd;				/* open file descriptor */
161243791Sdim	const struct netbuf *raddr;	/* servers address */
162243791Sdim	const rpcprog_t prog;			/* program number */
163243791Sdim	const rpcvers_t vers;			/* version number */
164243791Sdim	u_int sendsz;			/* buffer recv size */
165243791Sdim	u_int recvsz;			/* buffer send size */
166243791Sdim{
167243791Sdim	CLIENT *cl;			/* client handle */
168243791Sdim	struct ct_data *ct = NULL;	/* client handle */
169243791Sdim	struct timeval now;
170243791Sdim	struct rpc_msg call_msg;
171243791Sdim	static u_int32_t disrupt;
172243791Sdim	sigset_t mask;
173243791Sdim	sigset_t newmask;
174243791Sdim	struct sockaddr_storage ss;
175243791Sdim	socklen_t slen;
176243791Sdim	struct __rpc_sockinfo si;
177243791Sdim
178243791Sdim	if (disrupt == 0)
179243791Sdim		disrupt = (u_int32_t)(long)raddr;
180243791Sdim
181243791Sdim	cl = (CLIENT *)mem_alloc(sizeof (*cl));
182243791Sdim	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
183243791Sdim	if ((cl == (CLIENT *)NULL) || (ct == (struct ct_data *)NULL)) {
184243791Sdim		(void) syslog(LOG_ERR, clnt_vc_errstr,
185243791Sdim		    clnt_vc_str, __no_mem_str);
186243791Sdim		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
187243791Sdim		rpc_createerr.cf_error.re_errno = errno;
188243791Sdim		goto err;
189243791Sdim	}
190243791Sdim	ct->ct_addr.buf = NULL;
191243791Sdim	sigfillset(&newmask);
192243791Sdim	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
193243791Sdim	mutex_lock(&clnt_fd_lock);
194243791Sdim	if (vc_fd_locks == (int *) NULL) {
195243791Sdim		int cv_allocsz, fd_allocsz;
196243791Sdim		int dtbsize = __rpc_dtbsize();
197243791Sdim
198243791Sdim		fd_allocsz = dtbsize * sizeof (int);
199243791Sdim		vc_fd_locks = (int *) mem_alloc(fd_allocsz);
200243791Sdim		if (vc_fd_locks == (int *) NULL) {
201243791Sdim			mutex_unlock(&clnt_fd_lock);
202243791Sdim			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
203243791Sdim			goto err;
204243791Sdim		} else
205243791Sdim			memset(vc_fd_locks, '\0', fd_allocsz);
206243791Sdim
207243791Sdim		assert(vc_cv == (cond_t *) NULL);
208243791Sdim		cv_allocsz = dtbsize * sizeof (cond_t);
209243791Sdim		vc_cv = (cond_t *) mem_alloc(cv_allocsz);
210243791Sdim		if (vc_cv == (cond_t *) NULL) {
211243791Sdim			mem_free(vc_fd_locks, fd_allocsz);
212243791Sdim			vc_fd_locks = (int *) NULL;
213243791Sdim			mutex_unlock(&clnt_fd_lock);
214243791Sdim			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
215243791Sdim			goto err;
216243791Sdim		} else {
217243791Sdim			int i;
218243791Sdim
219243791Sdim			for (i = 0; i < dtbsize; i++)
220243791Sdim				cond_init(&vc_cv[i], 0, (void *) 0);
221243791Sdim		}
222243791Sdim	} else
223243791Sdim		assert(vc_cv != (cond_t *) NULL);
224243791Sdim
225243791Sdim	/*
226243791Sdim	 * XXX - fvdl connecting while holding a mutex?
227243791Sdim	 */
228243791Sdim	slen = sizeof ss;
229243791Sdim	if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
230243791Sdim		if (errno != ENOTCONN) {
231243791Sdim			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
232243791Sdim			rpc_createerr.cf_error.re_errno = errno;
233243791Sdim			mutex_unlock(&clnt_fd_lock);
234243791Sdim			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
235243791Sdim			goto err;
236243791Sdim		}
237243791Sdim		if (_connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){
238243791Sdim			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
239243791Sdim			rpc_createerr.cf_error.re_errno = errno;
240243791Sdim			mutex_unlock(&clnt_fd_lock);
241243791Sdim			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
242243791Sdim			goto err;
243243791Sdim		}
244243791Sdim	}
245243791Sdim	mutex_unlock(&clnt_fd_lock);
246243791Sdim	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
247243791Sdim	if (!__rpc_fd2sockinfo(fd, &si))
248243791Sdim		goto err;
249243791Sdim
250243791Sdim	ct->ct_closeit = FALSE;
251243791Sdim
252243791Sdim	/*
253243791Sdim	 * Set up private data struct
254243791Sdim	 */
255243791Sdim	ct->ct_fd = fd;
256243791Sdim	ct->ct_wait.tv_usec = 0;
257243791Sdim	ct->ct_waitset = FALSE;
258243791Sdim	ct->ct_addr.buf = malloc(raddr->maxlen);
259243791Sdim	if (ct->ct_addr.buf == NULL)
260243791Sdim		goto err;
261243791Sdim	memcpy(ct->ct_addr.buf, raddr->buf, raddr->len);
262243791Sdim	ct->ct_addr.len = raddr->maxlen;
263243791Sdim	ct->ct_addr.maxlen = raddr->maxlen;
264243791Sdim
265243791Sdim	/*
266243791Sdim	 * Initialize call message
267243791Sdim	 */
268243791Sdim	(void)gettimeofday(&now, NULL);
269243791Sdim	call_msg.rm_xid = ((u_int32_t)++disrupt) ^ __RPC_GETXID(&now);
270263509Sdim	call_msg.rm_direction = CALL;
271243791Sdim	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
272243791Sdim	call_msg.rm_call.cb_prog = (u_int32_t)prog;
273243791Sdim	call_msg.rm_call.cb_vers = (u_int32_t)vers;
274243791Sdim
275243791Sdim	/*
276243791Sdim	 * pre-serialize the static part of the call msg and stash it away
277243791Sdim	 */
278243791Sdim	xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
279243791Sdim	    XDR_ENCODE);
280243791Sdim	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
281243791Sdim		if (ct->ct_closeit) {
282243791Sdim			(void)_close(fd);
283243791Sdim		}
284243791Sdim		goto err;
285243791Sdim	}
286243791Sdim	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
287243791Sdim	XDR_DESTROY(&(ct->ct_xdrs));
288263509Sdim	assert(ct->ct_mpos + sizeof(uint32_t) <= MCALL_MSG_SIZE);
289243791Sdim
290243791Sdim	/*
291243791Sdim	 * Create a client handle which uses xdrrec for serialization
292243791Sdim	 * and authnone for authentication.
293243791Sdim	 */
294243791Sdim	cl->cl_ops = clnt_vc_ops();
295243791Sdim	cl->cl_private = ct;
296243791Sdim	cl->cl_auth = authnone_create();
297243791Sdim	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
298243791Sdim	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
299243791Sdim	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
300243791Sdim	    cl->cl_private, read_vc, write_vc);
301243791Sdim	return (cl);
302243791Sdim
303243791Sdimerr:
304243791Sdim	if (ct) {
305243791Sdim		if (ct->ct_addr.len)
306243791Sdim			mem_free(ct->ct_addr.buf, ct->ct_addr.len);
307243791Sdim		mem_free(ct, sizeof (struct ct_data));
308243791Sdim	}
309243791Sdim	if (cl)
310243791Sdim		mem_free(cl, sizeof (CLIENT));
311243791Sdim	return ((CLIENT *)NULL);
312243791Sdim}
313243791Sdim
314243791Sdimstatic enum clnt_stat
315243791Sdimclnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
316243791Sdim	CLIENT *cl;
317243791Sdim	rpcproc_t proc;
318243791Sdim	xdrproc_t xdr_args;
319243791Sdim	void *args_ptr;
320243791Sdim	xdrproc_t xdr_results;
321243791Sdim	void *results_ptr;
322243791Sdim	struct timeval timeout;
323243791Sdim{
324243791Sdim	struct ct_data *ct = (struct ct_data *) cl->cl_private;
325243791Sdim	XDR *xdrs = &(ct->ct_xdrs);
326243791Sdim	struct rpc_msg reply_msg;
327243791Sdim	u_int32_t x_id;
328243791Sdim	u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli;    /* yuk */
329243791Sdim	bool_t shipnow;
330243791Sdim	int refreshes = 2;
331243791Sdim	sigset_t mask, newmask;
332243791Sdim	int rpc_lock_value;
333243791Sdim	bool_t reply_stat;
334243791Sdim
335243791Sdim	assert(cl != NULL);
336243791Sdim
337243791Sdim	sigfillset(&newmask);
338243791Sdim	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
339243791Sdim	mutex_lock(&clnt_fd_lock);
340243791Sdim	while (vc_fd_locks[ct->ct_fd])
341243791Sdim		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
342243791Sdim	if (__isthreaded)
343243791Sdim                rpc_lock_value = 1;
344243791Sdim        else
345243791Sdim                rpc_lock_value = 0;
346243791Sdim	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
347243791Sdim	mutex_unlock(&clnt_fd_lock);
348243791Sdim	if (!ct->ct_waitset) {
349243791Sdim		/* If time is not within limits, we ignore it. */
350243791Sdim		if (time_not_ok(&timeout) == FALSE)
351243791Sdim			ct->ct_wait = timeout;
352243791Sdim	}
353243791Sdim
354243791Sdim	shipnow =
355243791Sdim	    (xdr_results == NULL && timeout.tv_sec == 0
356243791Sdim	    && timeout.tv_usec == 0) ? FALSE : TRUE;
357243791Sdim
358243791Sdimcall_again:
359243791Sdim	xdrs->x_op = XDR_ENCODE;
360243791Sdim	ct->ct_error.re_status = RPC_SUCCESS;
361243791Sdim	x_id = ntohl(--(*msg_x_id));
362243791Sdim
363243791Sdim	if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
364243791Sdim		if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
365243791Sdim		    (! XDR_PUTINT32(xdrs, &proc)) ||
366243791Sdim		    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
367243791Sdim		    (! (*xdr_args)(xdrs, args_ptr))) {
368243791Sdim			if (ct->ct_error.re_status == RPC_SUCCESS)
369243791Sdim				ct->ct_error.re_status = RPC_CANTENCODEARGS;
370243791Sdim			(void)xdrrec_endofrecord(xdrs, TRUE);
371243791Sdim			release_fd_lock(ct->ct_fd, mask);
372243791Sdim			return (ct->ct_error.re_status);
373243791Sdim		}
374243791Sdim	} else {
375243791Sdim		*(uint32_t *) &ct->ct_u.ct_mcallc[ct->ct_mpos] = htonl(proc);
376243791Sdim		if (! __rpc_gss_wrap(cl->cl_auth, ct->ct_u.ct_mcallc,
377243791Sdim			ct->ct_mpos + sizeof(uint32_t),
378243791Sdim			xdrs, xdr_args, args_ptr)) {
379243791Sdim			if (ct->ct_error.re_status == RPC_SUCCESS)
380263509Sdim				ct->ct_error.re_status = RPC_CANTENCODEARGS;
381243791Sdim			(void)xdrrec_endofrecord(xdrs, TRUE);
382243791Sdim			release_fd_lock(ct->ct_fd, mask);
383243791Sdim			return (ct->ct_error.re_status);
384243791Sdim		}
385243791Sdim	}
386243791Sdim	if (! xdrrec_endofrecord(xdrs, shipnow)) {
387243791Sdim		release_fd_lock(ct->ct_fd, mask);
388243791Sdim		return (ct->ct_error.re_status = RPC_CANTSEND);
389243791Sdim	}
390243791Sdim	if (! shipnow) {
391243791Sdim		release_fd_lock(ct->ct_fd, mask);
392243791Sdim		return (RPC_SUCCESS);
393243791Sdim	}
394243791Sdim	/*
395243791Sdim	 * Hack to provide rpc-based message passing
396243791Sdim	 */
397243791Sdim	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
398263509Sdim		release_fd_lock(ct->ct_fd, mask);
399243791Sdim		return(ct->ct_error.re_status = RPC_TIMEDOUT);
400243791Sdim	}
401243791Sdim
402243791Sdim
403243791Sdim	/*
404243791Sdim	 * Keep receiving until we get a valid transaction id
405243791Sdim	 */
406243791Sdim	xdrs->x_op = XDR_DECODE;
407243791Sdim	while (TRUE) {
408243791Sdim		reply_msg.acpted_rply.ar_verf = _null_auth;
409243791Sdim		reply_msg.acpted_rply.ar_results.where = NULL;
410243791Sdim		reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
411263509Sdim		if (! xdrrec_skiprecord(xdrs)) {
412263509Sdim			release_fd_lock(ct->ct_fd, mask);
413243791Sdim			return (ct->ct_error.re_status);
414243791Sdim		}
415243791Sdim		/* now decode and validate the response header */
416243791Sdim		if (! xdr_replymsg(xdrs, &reply_msg)) {
417243791Sdim			if (ct->ct_error.re_status == RPC_SUCCESS)
418243791Sdim				continue;
419243791Sdim			release_fd_lock(ct->ct_fd, mask);
420243791Sdim			return (ct->ct_error.re_status);
421243791Sdim		}
422243791Sdim		if (reply_msg.rm_xid == x_id)
423243791Sdim			break;
424243791Sdim	}
425243791Sdim
426243791Sdim	/*
427243791Sdim	 * process header
428243791Sdim	 */
429243791Sdim	_seterr_reply(&reply_msg, &(ct->ct_error));
430243791Sdim	if (ct->ct_error.re_status == RPC_SUCCESS) {
431243791Sdim		if (! AUTH_VALIDATE(cl->cl_auth,
432243791Sdim		    &reply_msg.acpted_rply.ar_verf)) {
433243791Sdim			ct->ct_error.re_status = RPC_AUTHERROR;
434243791Sdim			ct->ct_error.re_why = AUTH_INVALIDRESP;
435243791Sdim		} else {
436243791Sdim			if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
437243791Sdim				reply_stat = (*xdr_results)(xdrs, results_ptr);
438243791Sdim			} else {
439243791Sdim				reply_stat = __rpc_gss_unwrap(cl->cl_auth,
440243791Sdim				    xdrs, xdr_results, results_ptr);
441243791Sdim			}
442243791Sdim			if (! reply_stat) {
443243791Sdim				if (ct->ct_error.re_status == RPC_SUCCESS)
444243791Sdim					ct->ct_error.re_status =
445243791Sdim						RPC_CANTDECODERES;
446243791Sdim			}
447243791Sdim		}
448243791Sdim		/* free verifier ... */
449243791Sdim		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
450243791Sdim			xdrs->x_op = XDR_FREE;
451243791Sdim			(void)xdr_opaque_auth(xdrs,
452243791Sdim			    &(reply_msg.acpted_rply.ar_verf));
453243791Sdim		}
454243791Sdim	}  /* end successful completion */
455243791Sdim	else {
456243791Sdim		/* maybe our credentials need to be refreshed ... */
457243791Sdim		if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg))
458243791Sdim			goto call_again;
459243791Sdim	}  /* end of unsuccessful completion */
460243791Sdim	release_fd_lock(ct->ct_fd, mask);
461243791Sdim	return (ct->ct_error.re_status);
462243791Sdim}
463243791Sdim
464243791Sdimstatic void
465243791Sdimclnt_vc_geterr(cl, errp)
466243791Sdim	CLIENT *cl;
467243791Sdim	struct rpc_err *errp;
468243791Sdim{
469243791Sdim	struct ct_data *ct;
470243791Sdim
471243791Sdim	assert(cl != NULL);
472243791Sdim	assert(errp != NULL);
473243791Sdim
474243791Sdim	ct = (struct ct_data *) cl->cl_private;
475243791Sdim	*errp = ct->ct_error;
476243791Sdim}
477243791Sdim
478243791Sdimstatic bool_t
479243791Sdimclnt_vc_freeres(cl, xdr_res, res_ptr)
480243791Sdim	CLIENT *cl;
481243791Sdim	xdrproc_t xdr_res;
482243791Sdim	void *res_ptr;
483243791Sdim{
484243791Sdim	struct ct_data *ct;
485243791Sdim	XDR *xdrs;
486243791Sdim	bool_t dummy;
487243791Sdim	sigset_t mask;
488252723Sdim	sigset_t newmask;
489243791Sdim
490243791Sdim	assert(cl != NULL);
491243791Sdim
492243791Sdim	ct = (struct ct_data *)cl->cl_private;
493243791Sdim	xdrs = &(ct->ct_xdrs);
494252723Sdim
495243791Sdim	sigfillset(&newmask);
496243791Sdim	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
497243791Sdim	mutex_lock(&clnt_fd_lock);
498243791Sdim	while (vc_fd_locks[ct->ct_fd])
499243791Sdim		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
500243791Sdim	xdrs->x_op = XDR_FREE;
501243791Sdim	dummy = (*xdr_res)(xdrs, res_ptr);
502243791Sdim	mutex_unlock(&clnt_fd_lock);
503243791Sdim	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
504243791Sdim	cond_signal(&vc_cv[ct->ct_fd]);
505243791Sdim
506243791Sdim	return dummy;
507243791Sdim}
508243791Sdim
509243791Sdim/*ARGSUSED*/
510243791Sdimstatic void
511243791Sdimclnt_vc_abort(cl)
512243791Sdim	CLIENT *cl;
513243791Sdim{
514243791Sdim}
515243791Sdim
516243791Sdimstatic bool_t
517243791Sdimclnt_vc_control(cl, request, info)
518243791Sdim	CLIENT *cl;
519243791Sdim	u_int request;
520243791Sdim	void *info;
521243791Sdim{
522243791Sdim	struct ct_data *ct;
523243791Sdim	void *infop = info;
524243791Sdim	sigset_t mask;
525243791Sdim	sigset_t newmask;
526243791Sdim	int rpc_lock_value;
527243791Sdim
528243791Sdim	assert(cl != NULL);
529243791Sdim
530243791Sdim	ct = (struct ct_data *)cl->cl_private;
531243791Sdim
532243791Sdim	sigfillset(&newmask);
533243791Sdim	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
534243791Sdim	mutex_lock(&clnt_fd_lock);
535243791Sdim	while (vc_fd_locks[ct->ct_fd])
536243791Sdim		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
537243791Sdim	if (__isthreaded)
538243791Sdim                rpc_lock_value = 1;
539243791Sdim        else
540243791Sdim                rpc_lock_value = 0;
541243791Sdim	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
542243791Sdim	mutex_unlock(&clnt_fd_lock);
543243791Sdim
544243791Sdim	switch (request) {
545243791Sdim	case CLSET_FD_CLOSE:
546243791Sdim		ct->ct_closeit = TRUE;
547243791Sdim		release_fd_lock(ct->ct_fd, mask);
548243791Sdim		return (TRUE);
549243791Sdim	case CLSET_FD_NCLOSE:
550243791Sdim		ct->ct_closeit = FALSE;
551243791Sdim		release_fd_lock(ct->ct_fd, mask);
552243791Sdim		return (TRUE);
553243791Sdim	default:
554243791Sdim		break;
555243791Sdim	}
556243791Sdim
557243791Sdim	/* for other requests which use info */
558243791Sdim	if (info == NULL) {
559243791Sdim		release_fd_lock(ct->ct_fd, mask);
560243791Sdim		return (FALSE);
561243791Sdim	}
562243791Sdim	switch (request) {
563243791Sdim	case CLSET_TIMEOUT:
564243791Sdim		if (time_not_ok((struct timeval *)info)) {
565243791Sdim			release_fd_lock(ct->ct_fd, mask);
566243791Sdim			return (FALSE);
567243791Sdim		}
568243791Sdim		ct->ct_wait = *(struct timeval *)infop;
569243791Sdim		ct->ct_waitset = TRUE;
570243791Sdim		break;
571243791Sdim	case CLGET_TIMEOUT:
572243791Sdim		*(struct timeval *)infop = ct->ct_wait;
573243791Sdim		break;
574243791Sdim	case CLGET_SERVER_ADDR:
575243791Sdim		(void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
576243791Sdim		break;
577243791Sdim	case CLGET_FD:
578243791Sdim		*(int *)info = ct->ct_fd;
579243791Sdim		break;
580243791Sdim	case CLGET_SVC_ADDR:
581243791Sdim		/* The caller should not free this memory area */
582243791Sdim		*(struct netbuf *)info = ct->ct_addr;
583243791Sdim		break;
584243791Sdim	case CLSET_SVC_ADDR:		/* set to new address */
585243791Sdim		release_fd_lock(ct->ct_fd, mask);
586243791Sdim		return (FALSE);
587243791Sdim	case CLGET_XID:
588243791Sdim		/*
589243791Sdim		 * use the knowledge that xid is the
590243791Sdim		 * first element in the call structure
591243791Sdim		 * This will get the xid of the PREVIOUS call
592243791Sdim		 */
593243791Sdim		*(u_int32_t *)info =
594243791Sdim		    ntohl(*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli);
595243791Sdim		break;
596243791Sdim	case CLSET_XID:
597243791Sdim		/* This will set the xid of the NEXT call */
598243791Sdim		*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli =
599243791Sdim		    htonl(*((u_int32_t *)info) + 1);
600243791Sdim		/* increment by 1 as clnt_vc_call() decrements once */
601243791Sdim		break;
602243791Sdim	case CLGET_VERS:
603243791Sdim		/*
604243791Sdim		 * This RELIES on the information that, in the call body,
605243791Sdim		 * the version number field is the fifth field from the
606243791Sdim		 * begining of the RPC header. MUST be changed if the
607243791Sdim		 * call_struct is changed
608243791Sdim		 */
609243791Sdim		*(u_int32_t *)info =
610243791Sdim		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
611243791Sdim		    4 * BYTES_PER_XDR_UNIT));
612243791Sdim		break;
613243791Sdim
614243791Sdim	case CLSET_VERS:
615243791Sdim		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
616243791Sdim		    4 * BYTES_PER_XDR_UNIT) =
617243791Sdim		    htonl(*(u_int32_t *)info);
618243791Sdim		break;
619243791Sdim
620243791Sdim	case CLGET_PROG:
621243791Sdim		/*
622243791Sdim		 * This RELIES on the information that, in the call body,
623243791Sdim		 * the program number field is the fourth field from the
624243791Sdim		 * begining of the RPC header. MUST be changed if the
625243791Sdim		 * call_struct is changed
626263509Sdim		 */
627243791Sdim		*(u_int32_t *)info =
628243791Sdim		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
629243791Sdim		    3 * BYTES_PER_XDR_UNIT));
630243791Sdim		break;
631243791Sdim
632243791Sdim	case CLSET_PROG:
633243791Sdim		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
634243791Sdim		    3 * BYTES_PER_XDR_UNIT) =
635243791Sdim		    htonl(*(u_int32_t *)info);
636243791Sdim		break;
637243791Sdim
638243791Sdim	default:
639243791Sdim		release_fd_lock(ct->ct_fd, mask);
640243791Sdim		return (FALSE);
641243791Sdim	}
642243791Sdim	release_fd_lock(ct->ct_fd, mask);
643243791Sdim	return (TRUE);
644243791Sdim}
645243791Sdim
646243791Sdim
647243791Sdimstatic void
648243791Sdimclnt_vc_destroy(cl)
649243791Sdim	CLIENT *cl;
650243791Sdim{
651243791Sdim	struct ct_data *ct = (struct ct_data *) cl->cl_private;
652243791Sdim	int ct_fd = ct->ct_fd;
653243791Sdim	sigset_t mask;
654243791Sdim	sigset_t newmask;
655243791Sdim
656243791Sdim	assert(cl != NULL);
657243791Sdim
658243791Sdim	ct = (struct ct_data *) cl->cl_private;
659243791Sdim
660243791Sdim	sigfillset(&newmask);
661243791Sdim	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
662243791Sdim	mutex_lock(&clnt_fd_lock);
663243791Sdim	while (vc_fd_locks[ct_fd])
664243791Sdim		cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
665243791Sdim	if (ct->ct_closeit && ct->ct_fd != -1) {
666243791Sdim		(void)_close(ct->ct_fd);
667243791Sdim	}
668243791Sdim	XDR_DESTROY(&(ct->ct_xdrs));
669243791Sdim	if (ct->ct_addr.buf)
670243791Sdim		free(ct->ct_addr.buf);
671243791Sdim	mem_free(ct, sizeof(struct ct_data));
672243791Sdim	if (cl->cl_netid && cl->cl_netid[0])
673243791Sdim		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
674243791Sdim	if (cl->cl_tp && cl->cl_tp[0])
675243791Sdim		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
676243791Sdim	mem_free(cl, sizeof(CLIENT));
677243791Sdim	mutex_unlock(&clnt_fd_lock);
678243791Sdim	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
679243791Sdim	cond_signal(&vc_cv[ct_fd]);
680243791Sdim}
681243791Sdim
682243791Sdim/*
683243791Sdim * Interface between xdr serializer and tcp connection.
684243791Sdim * Behaves like the system calls, read & write, but keeps some error state
685243791Sdim * around for the rpc level.
686243791Sdim */
687243791Sdimstatic int
688243791Sdimread_vc(ctp, buf, len)
689243791Sdim	void *ctp;
690243791Sdim	void *buf;
691243791Sdim	int len;
692243791Sdim{
693243791Sdim	struct sockaddr sa;
694243791Sdim	socklen_t sal;
695243791Sdim	struct ct_data *ct = (struct ct_data *)ctp;
696243791Sdim	struct pollfd fd;
697243791Sdim	int milliseconds = (int)((ct->ct_wait.tv_sec * 1000) +
698243791Sdim	    (ct->ct_wait.tv_usec / 1000));
699243791Sdim
700243791Sdim	if (len == 0)
701243791Sdim		return (0);
702243791Sdim	fd.fd = ct->ct_fd;
703243791Sdim	fd.events = POLLIN;
704243791Sdim	for (;;) {
705243791Sdim		switch (_poll(&fd, 1, milliseconds)) {
706243791Sdim		case 0:
707243791Sdim			ct->ct_error.re_status = RPC_TIMEDOUT;
708243791Sdim			return (-1);
709243791Sdim
710243791Sdim		case -1:
711243791Sdim			if (errno == EINTR)
712243791Sdim				continue;
713243791Sdim			ct->ct_error.re_status = RPC_CANTRECV;
714243791Sdim			ct->ct_error.re_errno = errno;
715243791Sdim			return (-1);
716243791Sdim		}
717243791Sdim		break;
718243791Sdim	}
719243791Sdim
720243791Sdim	sal = sizeof(sa);
721243791Sdim	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
722243791Sdim	    (sa.sa_family == AF_LOCAL)) {
723243791Sdim		len = __msgread(ct->ct_fd, buf, (size_t)len);
724263509Sdim	} else {
725243791Sdim		len = _read(ct->ct_fd, buf, (size_t)len);
726243791Sdim	}
727243791Sdim
728243791Sdim	switch (len) {
729243791Sdim	case 0:
730243791Sdim		/* premature eof */
731243791Sdim		ct->ct_error.re_errno = ECONNRESET;
732243791Sdim		ct->ct_error.re_status = RPC_CANTRECV;
733243791Sdim		len = -1;  /* it's really an error */
734243791Sdim		break;
735243791Sdim
736243791Sdim	case -1:
737243791Sdim		ct->ct_error.re_errno = errno;
738243791Sdim		ct->ct_error.re_status = RPC_CANTRECV;
739243791Sdim		break;
740243791Sdim	}
741243791Sdim	return (len);
742243791Sdim}
743243791Sdim
744243791Sdimstatic int
745243791Sdimwrite_vc(ctp, buf, len)
746243791Sdim	void *ctp;
747243791Sdim	void *buf;
748243791Sdim	int len;
749243791Sdim{
750243791Sdim	struct sockaddr sa;
751243791Sdim	socklen_t sal;
752243791Sdim	struct ct_data *ct = (struct ct_data *)ctp;
753243791Sdim	int i, cnt;
754243791Sdim
755243791Sdim	sal = sizeof(sa);
756243791Sdim	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
757243791Sdim	    (sa.sa_family == AF_LOCAL)) {
758243791Sdim		for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
759243791Sdim			if ((i = __msgwrite(ct->ct_fd, buf,
760243791Sdim			     (size_t)cnt)) == -1) {
761243791Sdim				ct->ct_error.re_errno = errno;
762243791Sdim				ct->ct_error.re_status = RPC_CANTSEND;
763243791Sdim				return (-1);
764243791Sdim			}
765243791Sdim		}
766243791Sdim	} else {
767243791Sdim		for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
768243791Sdim			if ((i = _write(ct->ct_fd, buf, (size_t)cnt)) == -1) {
769243791Sdim				ct->ct_error.re_errno = errno;
770243791Sdim				ct->ct_error.re_status = RPC_CANTSEND;
771243791Sdim				return (-1);
772243791Sdim			}
773243791Sdim		}
774243791Sdim	}
775243791Sdim	return (len);
776243791Sdim}
777243791Sdim
778243791Sdimstatic struct clnt_ops *
779243791Sdimclnt_vc_ops()
780243791Sdim{
781243791Sdim	static struct clnt_ops ops;
782243791Sdim	sigset_t mask, newmask;
783243791Sdim
784243791Sdim	/* VARIABLES PROTECTED BY ops_lock: ops */
785243791Sdim
786243791Sdim	sigfillset(&newmask);
787243791Sdim	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
788243791Sdim	mutex_lock(&ops_lock);
789243791Sdim	if (ops.cl_call == NULL) {
790243791Sdim		ops.cl_call = clnt_vc_call;
791243791Sdim		ops.cl_abort = clnt_vc_abort;
792243791Sdim		ops.cl_geterr = clnt_vc_geterr;
793243791Sdim		ops.cl_freeres = clnt_vc_freeres;
794243791Sdim		ops.cl_destroy = clnt_vc_destroy;
795243791Sdim		ops.cl_control = clnt_vc_control;
796243791Sdim	}
797243791Sdim	mutex_unlock(&ops_lock);
798243791Sdim	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
799243791Sdim	return (&ops);
800243791Sdim}
801243791Sdim
802243791Sdim/*
803243791Sdim * Make sure that the time is not garbage.   -1 value is disallowed.
804243791Sdim * Note this is different from time_not_ok in clnt_dg.c
805243791Sdim */
806243791Sdimstatic bool_t
807243791Sdimtime_not_ok(t)
808243791Sdim	struct timeval *t;
809243791Sdim{
810243791Sdim	return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
811243791Sdim		t->tv_usec <= -1 || t->tv_usec > 1000000);
812243791Sdim}
813243791Sdim
814243791Sdimstatic int
815243791Sdim__msgread(sock, buf, cnt)
816243791Sdim	int sock;
817243791Sdim	void *buf;
818243791Sdim	size_t cnt;
819243791Sdim{
820243791Sdim	struct iovec iov[1];
821243791Sdim	struct msghdr msg;
822243791Sdim	union {
823243791Sdim		struct cmsghdr cmsg;
824243791Sdim		char control[CMSG_SPACE(sizeof(struct cmsgcred))];
825243791Sdim	} cm;
826243791Sdim
827243791Sdim	bzero((char *)&cm, sizeof(cm));
828243791Sdim	iov[0].iov_base = buf;
829243791Sdim	iov[0].iov_len = cnt;
830243791Sdim
831243791Sdim	msg.msg_iov = iov;
832243791Sdim	msg.msg_iovlen = 1;
833243791Sdim	msg.msg_name = NULL;
834243791Sdim	msg.msg_namelen = 0;
835243791Sdim	msg.msg_control = (caddr_t)&cm;
836243791Sdim	msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
837243791Sdim	msg.msg_flags = 0;
838243791Sdim
839243791Sdim	return(_recvmsg(sock, &msg, 0));
840243791Sdim}
841243791Sdim
842243791Sdimstatic int
843243791Sdim__msgwrite(sock, buf, cnt)
844243791Sdim	int sock;
845243791Sdim	void *buf;
846243791Sdim	size_t cnt;
847243791Sdim{
848243791Sdim	struct iovec iov[1];
849243791Sdim	struct msghdr msg;
850243791Sdim	union {
851243791Sdim		struct cmsghdr cmsg;
852243791Sdim		char control[CMSG_SPACE(sizeof(struct cmsgcred))];
853243791Sdim	} cm;
854243791Sdim
855243791Sdim	bzero((char *)&cm, sizeof(cm));
856243791Sdim	iov[0].iov_base = buf;
857243791Sdim	iov[0].iov_len = cnt;
858243791Sdim
859243791Sdim	cm.cmsg.cmsg_type = SCM_CREDS;
860243791Sdim	cm.cmsg.cmsg_level = SOL_SOCKET;
861243791Sdim	cm.cmsg.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
862243791Sdim
863243791Sdim	msg.msg_iov = iov;
864243791Sdim	msg.msg_iovlen = 1;
865243791Sdim	msg.msg_name = NULL;
866243791Sdim	msg.msg_namelen = 0;
867243791Sdim	msg.msg_control = (caddr_t)&cm;
868243791Sdim	msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
869243791Sdim	msg.msg_flags = 0;
870243791Sdim
871243791Sdim	return(_sendmsg(sock, &msg, 0));
872243791Sdim}
873243791Sdim