174462Salfred/*	$NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $	*/
274462Salfred
3261046Smav/*-
4261046Smav * Copyright (c) 2009, Sun Microsystems, Inc.
5261046Smav * All rights reserved.
6261046Smav *
7261046Smav * Redistribution and use in source and binary forms, with or without
8261046Smav * modification, are permitted provided that the following conditions are met:
9261046Smav * - Redistributions of source code must retain the above copyright notice,
10261046Smav *   this list of conditions and the following disclaimer.
11261046Smav * - Redistributions in binary form must reproduce the above copyright notice,
12261046Smav *   this list of conditions and the following disclaimer in the documentation
13261046Smav *   and/or other materials provided with the distribution.
14261046Smav * - Neither the name of Sun Microsystems, Inc. nor the names of its
15261046Smav *   contributors may be used to endorse or promote products derived
16261046Smav *   from this software without specific prior written permission.
1774462Salfred *
18261046Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19261046Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20261046Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21261046Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22261046Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23261046Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24261046Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25261046Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26261046Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27261046Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28261046Smav * POSSIBILITY OF SUCH DAMAGE.
2974462Salfred */
3074462Salfred
3174462Salfred#if defined(LIBC_SCCS) && !defined(lint)
32136581Sobrienstatic char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
3374462Salfredstatic char *sccsid = "@(#)clnt_tcp.c	2.2 88/08/01 4.0 RPCSRC";
34136581Sobrienstatic char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro";
3574462Salfred#endif
3692990Sobrien#include <sys/cdefs.h>
3792990Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/rpc/clnt_vc.c 309498 2016-12-03 18:40:39Z ngie $");
3874462Salfred
3974462Salfred/*
4074462Salfred * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
4174462Salfred *
4274462Salfred * Copyright (C) 1984, Sun Microsystems, Inc.
4374462Salfred *
4474462Salfred * TCP based RPC supports 'batched calls'.
4574462Salfred * A sequence of calls may be batched-up in a send buffer.  The rpc call
4674462Salfred * return immediately to the client even though the call was not necessarily
4774462Salfred * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
4874462Salfred * the rpc timeout value is zero (see clnt.h, rpc).
4974462Salfred *
5074462Salfred * Clients should NOT casually batch calls that in fact return results; that is,
5174462Salfred * the server side should be aware that a call is batched and not produce any
5274462Salfred * return message.  Batched calls that produce many result messages can
5374462Salfred * deadlock (netlock) the client and the server....
5474462Salfred *
5574462Salfred * Now go hang yourself.
5674462Salfred */
5774462Salfred
5875094Siedowse#include "namespace.h"
5974462Salfred#include "reentrant.h"
6074462Salfred#include <sys/types.h>
6174462Salfred#include <sys/poll.h>
6274462Salfred#include <sys/syslog.h>
6374462Salfred#include <sys/socket.h>
6474462Salfred#include <sys/un.h>
6574462Salfred#include <sys/uio.h>
6674462Salfred
6790868Smike#include <arpa/inet.h>
6874462Salfred#include <assert.h>
6974462Salfred#include <err.h>
7074462Salfred#include <errno.h>
7174462Salfred#include <netdb.h>
7274462Salfred#include <stdio.h>
7374462Salfred#include <stdlib.h>
7474462Salfred#include <string.h>
7574462Salfred#include <unistd.h>
7674462Salfred#include <signal.h>
7774462Salfred
7874462Salfred#include <rpc/rpc.h>
79181344Sdfr#include <rpc/rpcsec_gss.h>
8074462Salfred#include "un-namespace.h"
8174462Salfred#include "rpc_com.h"
82156090Sdeischen#include "mt_misc.h"
8374462Salfred
8474462Salfred#define MCALL_MSG_SIZE 24
8574462Salfred
8674660Salfredstruct cmessage {
8774660Salfred        struct cmsghdr cmsg;
8874660Salfred        struct cmsgcred cmcred;
8974660Salfred};
9074660Salfred
9195658Sdesstatic enum clnt_stat clnt_vc_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
9295658Sdes    xdrproc_t, void *, struct timeval);
9392905Sobrienstatic void clnt_vc_geterr(CLIENT *, struct rpc_err *);
9495658Sdesstatic bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *);
9592905Sobrienstatic void clnt_vc_abort(CLIENT *);
9699996Salfredstatic bool_t clnt_vc_control(CLIENT *, u_int, void *);
9792905Sobrienstatic void clnt_vc_destroy(CLIENT *);
9892905Sobrienstatic struct clnt_ops *clnt_vc_ops(void);
9992905Sobrienstatic bool_t time_not_ok(struct timeval *);
10095658Sdesstatic int read_vc(void *, void *, int);
10195658Sdesstatic int write_vc(void *, void *, int);
10274462Salfredstatic int __msgwrite(int, void *, size_t);
10374462Salfredstatic int __msgread(int, void *, size_t);
10474462Salfred
10574462Salfredstruct ct_data {
10674462Salfred	int		ct_fd;		/* connection's fd */
10774462Salfred	bool_t		ct_closeit;	/* close it on destroy */
10874462Salfred	struct timeval	ct_wait;	/* wait interval in milliseconds */
10974462Salfred	bool_t          ct_waitset;	/* wait set by clnt_control? */
11074462Salfred	struct netbuf	ct_addr;	/* remote addr */
11174462Salfred	struct rpc_err	ct_error;
11274462Salfred	union {
11374462Salfred		char	ct_mcallc[MCALL_MSG_SIZE];	/* marshalled callmsg */
11474462Salfred		u_int32_t ct_mcalli;
11574462Salfred	} ct_u;
11674462Salfred	u_int		ct_mpos;	/* pos after marshal */
11774462Salfred	XDR		ct_xdrs;	/* XDR stream */
11874462Salfred};
11974462Salfred
12074462Salfred/*
12174462Salfred *      This machinery implements per-fd locks for MT-safety.  It is not
12274462Salfred *      sufficient to do per-CLIENT handle locks for MT-safety because a
12374462Salfred *      user may create more than one CLIENT handle with the same fd behind
12474462Salfred *      it.  Therfore, we allocate an array of flags (vc_fd_locks), protected
12574462Salfred *      by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
12674462Salfred *      similarly protected.  Vc_fd_lock[fd] == 1 => a call is activte on some
12774462Salfred *      CLIENT handle created for that fd.
12874462Salfred *      The current implementation holds locks across the entire RPC and reply.
12974462Salfred *      Yes, this is silly, and as soon as this code is proven to work, this
13074462Salfred *      should be the first thing fixed.  One step at a time.
13174462Salfred */
13274462Salfredstatic int      *vc_fd_locks;
13374462Salfredstatic cond_t   *vc_cv;
13474462Salfred#define release_fd_lock(fd, mask) {	\
13574462Salfred	mutex_lock(&clnt_fd_lock);	\
13675144Siedowse	vc_fd_locks[fd] = 0;		\
13774462Salfred	mutex_unlock(&clnt_fd_lock);	\
13874462Salfred	thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL);	\
13974462Salfred	cond_signal(&vc_cv[fd]);	\
14074462Salfred}
14174462Salfred
14274462Salfredstatic const char clnt_vc_errstr[] = "%s : %s";
14374462Salfredstatic const char clnt_vc_str[] = "clnt_vc_create";
14474462Salfredstatic const char clnt_read_vc_str[] = "read_vc";
14574462Salfredstatic const char __no_mem_str[] = "out of memory";
14674462Salfred
14774462Salfred/*
14874462Salfred * Create a client handle for a connection.
14974462Salfred * Default options are set, which the user can change using clnt_control()'s.
15074462Salfred * The rpc/vc package does buffering similar to stdio, so the client
15174462Salfred * must pick send and receive buffer sizes, 0 => use the default.
15274462Salfred * NB: fd is copied into a private area.
15374462Salfred * NB: The rpch->cl_auth is set null authentication. Caller may wish to
15474462Salfred * set this something more useful.
15574462Salfred *
15674462Salfred * fd should be an open socket
157309487Sngie *
158309487Sngie * fd - open file descriptor
159309487Sngie * raddr - servers address
160309487Sngie * prog  - program number
161309487Sngie * vers  - version number
162309487Sngie * sendsz - buffer send size
163309487Sngie * recvsz - buffer recv size
16474462Salfred */
16574462SalfredCLIENT *
166309487Sngieclnt_vc_create(int fd, const struct netbuf *raddr, const rpcprog_t prog,
167309487Sngie    const rpcvers_t vers, u_int sendsz, u_int recvsz)
16874462Salfred{
16974462Salfred	CLIENT *cl;			/* client handle */
17074462Salfred	struct ct_data *ct = NULL;	/* client handle */
17174462Salfred	struct timeval now;
17274462Salfred	struct rpc_msg call_msg;
17374462Salfred	static u_int32_t disrupt;
17474462Salfred	sigset_t mask;
17574462Salfred	sigset_t newmask;
17674462Salfred	struct sockaddr_storage ss;
17774462Salfred	socklen_t slen;
17874462Salfred	struct __rpc_sockinfo si;
17974462Salfred
18074462Salfred	if (disrupt == 0)
18174462Salfred		disrupt = (u_int32_t)(long)raddr;
18274462Salfred
18374462Salfred	cl = (CLIENT *)mem_alloc(sizeof (*cl));
18474462Salfred	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
18574462Salfred	if ((cl == (CLIENT *)NULL) || (ct == (struct ct_data *)NULL)) {
18674462Salfred		(void) syslog(LOG_ERR, clnt_vc_errstr,
18774462Salfred		    clnt_vc_str, __no_mem_str);
18874462Salfred		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
18974462Salfred		rpc_createerr.cf_error.re_errno = errno;
19074462Salfred		goto err;
19174462Salfred	}
19274462Salfred	ct->ct_addr.buf = NULL;
19374462Salfred	sigfillset(&newmask);
19474462Salfred	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
19574462Salfred	mutex_lock(&clnt_fd_lock);
19674462Salfred	if (vc_fd_locks == (int *) NULL) {
19774462Salfred		int cv_allocsz, fd_allocsz;
19874462Salfred		int dtbsize = __rpc_dtbsize();
19974462Salfred
20074462Salfred		fd_allocsz = dtbsize * sizeof (int);
20174462Salfred		vc_fd_locks = (int *) mem_alloc(fd_allocsz);
20274462Salfred		if (vc_fd_locks == (int *) NULL) {
20374462Salfred			mutex_unlock(&clnt_fd_lock);
20474462Salfred			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
20574462Salfred			goto err;
20674462Salfred		} else
20774462Salfred			memset(vc_fd_locks, '\0', fd_allocsz);
20874462Salfred
20974462Salfred		assert(vc_cv == (cond_t *) NULL);
21074462Salfred		cv_allocsz = dtbsize * sizeof (cond_t);
21174462Salfred		vc_cv = (cond_t *) mem_alloc(cv_allocsz);
21274462Salfred		if (vc_cv == (cond_t *) NULL) {
21374462Salfred			mem_free(vc_fd_locks, fd_allocsz);
21474462Salfred			vc_fd_locks = (int *) NULL;
21574462Salfred			mutex_unlock(&clnt_fd_lock);
21674462Salfred			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
21774462Salfred			goto err;
21874462Salfred		} else {
21974462Salfred			int i;
22074462Salfred
22174462Salfred			for (i = 0; i < dtbsize; i++)
22274462Salfred				cond_init(&vc_cv[i], 0, (void *) 0);
22374462Salfred		}
22474462Salfred	} else
22574462Salfred		assert(vc_cv != (cond_t *) NULL);
22674462Salfred
22774462Salfred	/*
22874462Salfred	 * XXX - fvdl connecting while holding a mutex?
22974462Salfred	 */
23074462Salfred	slen = sizeof ss;
23174462Salfred	if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
23274462Salfred		if (errno != ENOTCONN) {
23374462Salfred			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
23474462Salfred			rpc_createerr.cf_error.re_errno = errno;
23574462Salfred			mutex_unlock(&clnt_fd_lock);
23675097Siedowse			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
23774462Salfred			goto err;
23874462Salfred		}
23974462Salfred		if (_connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){
24074462Salfred			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
24174462Salfred			rpc_createerr.cf_error.re_errno = errno;
24274462Salfred			mutex_unlock(&clnt_fd_lock);
24375097Siedowse			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
24474462Salfred			goto err;
24574462Salfred		}
24674462Salfred	}
24774462Salfred	mutex_unlock(&clnt_fd_lock);
248162190Smbr	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
24974462Salfred	if (!__rpc_fd2sockinfo(fd, &si))
25074462Salfred		goto err;
25174462Salfred
25274462Salfred	ct->ct_closeit = FALSE;
25374462Salfred
25474462Salfred	/*
25574462Salfred	 * Set up private data struct
25674462Salfred	 */
25774462Salfred	ct->ct_fd = fd;
25874462Salfred	ct->ct_wait.tv_usec = 0;
25974462Salfred	ct->ct_waitset = FALSE;
26074462Salfred	ct->ct_addr.buf = malloc(raddr->maxlen);
26174462Salfred	if (ct->ct_addr.buf == NULL)
26274462Salfred		goto err;
26377588Siedowse	memcpy(ct->ct_addr.buf, raddr->buf, raddr->len);
264247550Skevlo	ct->ct_addr.len = raddr->len;
26574462Salfred	ct->ct_addr.maxlen = raddr->maxlen;
26674462Salfred
26774462Salfred	/*
26874462Salfred	 * Initialize call message
26974462Salfred	 */
27074462Salfred	(void)gettimeofday(&now, NULL);
27174462Salfred	call_msg.rm_xid = ((u_int32_t)++disrupt) ^ __RPC_GETXID(&now);
27274462Salfred	call_msg.rm_direction = CALL;
27374462Salfred	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
27474462Salfred	call_msg.rm_call.cb_prog = (u_int32_t)prog;
27574462Salfred	call_msg.rm_call.cb_vers = (u_int32_t)vers;
27674462Salfred
27774462Salfred	/*
27874462Salfred	 * pre-serialize the static part of the call msg and stash it away
27974462Salfred	 */
28074462Salfred	xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
28174462Salfred	    XDR_ENCODE);
28274462Salfred	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
28374462Salfred		if (ct->ct_closeit) {
28474462Salfred			(void)_close(fd);
28574462Salfred		}
28674462Salfred		goto err;
28774462Salfred	}
28874462Salfred	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
28974462Salfred	XDR_DESTROY(&(ct->ct_xdrs));
290181344Sdfr	assert(ct->ct_mpos + sizeof(uint32_t) <= MCALL_MSG_SIZE);
29174462Salfred
29274462Salfred	/*
29374462Salfred	 * Create a client handle which uses xdrrec for serialization
29474462Salfred	 * and authnone for authentication.
29574462Salfred	 */
29674462Salfred	cl->cl_ops = clnt_vc_ops();
29774462Salfred	cl->cl_private = ct;
29874462Salfred	cl->cl_auth = authnone_create();
29974462Salfred	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
30074462Salfred	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
30174462Salfred	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
30274462Salfred	    cl->cl_private, read_vc, write_vc);
30374462Salfred	return (cl);
30474462Salfred
30574462Salfrederr:
306266243Sbrueffer	if (ct) {
307266243Sbrueffer		if (ct->ct_addr.len)
308266243Sbrueffer			mem_free(ct->ct_addr.buf, ct->ct_addr.len);
309266243Sbrueffer		mem_free(ct, sizeof (struct ct_data));
31074462Salfred	}
311266243Sbrueffer	if (cl)
312266243Sbrueffer		mem_free(cl, sizeof (CLIENT));
31374462Salfred	return ((CLIENT *)NULL);
31474462Salfred}
31574462Salfred
31674462Salfredstatic enum clnt_stat
317309487Sngieclnt_vc_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xdr_args, void *args_ptr,
318309487Sngie    xdrproc_t xdr_results, void *results_ptr, struct timeval timeout)
31974462Salfred{
32074462Salfred	struct ct_data *ct = (struct ct_data *) cl->cl_private;
32174462Salfred	XDR *xdrs = &(ct->ct_xdrs);
32274462Salfred	struct rpc_msg reply_msg;
32374462Salfred	u_int32_t x_id;
32474462Salfred	u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli;    /* yuk */
32574462Salfred	bool_t shipnow;
32674462Salfred	int refreshes = 2;
32774462Salfred	sigset_t mask, newmask;
32874462Salfred	int rpc_lock_value;
329181344Sdfr	bool_t reply_stat;
33074462Salfred
33174462Salfred	assert(cl != NULL);
33274462Salfred
33374462Salfred	sigfillset(&newmask);
33474462Salfred	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
33574462Salfred	mutex_lock(&clnt_fd_lock);
33674462Salfred	while (vc_fd_locks[ct->ct_fd])
33774462Salfred		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
33874462Salfred	if (__isthreaded)
33974462Salfred                rpc_lock_value = 1;
34074462Salfred        else
34174462Salfred                rpc_lock_value = 0;
34274462Salfred	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
34374462Salfred	mutex_unlock(&clnt_fd_lock);
34474462Salfred	if (!ct->ct_waitset) {
34574462Salfred		/* If time is not within limits, we ignore it. */
34674462Salfred		if (time_not_ok(&timeout) == FALSE)
34774462Salfred			ct->ct_wait = timeout;
34874462Salfred	}
34974462Salfred
35074462Salfred	shipnow =
35174462Salfred	    (xdr_results == NULL && timeout.tv_sec == 0
35274462Salfred	    && timeout.tv_usec == 0) ? FALSE : TRUE;
35374462Salfred
35474462Salfredcall_again:
35574462Salfred	xdrs->x_op = XDR_ENCODE;
35674462Salfred	ct->ct_error.re_status = RPC_SUCCESS;
35774462Salfred	x_id = ntohl(--(*msg_x_id));
35874462Salfred
359181344Sdfr	if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
360181344Sdfr		if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
361181344Sdfr		    (! XDR_PUTINT32(xdrs, &proc)) ||
362181344Sdfr		    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
363181344Sdfr		    (! (*xdr_args)(xdrs, args_ptr))) {
364181344Sdfr			if (ct->ct_error.re_status == RPC_SUCCESS)
365181344Sdfr				ct->ct_error.re_status = RPC_CANTENCODEARGS;
366181344Sdfr			(void)xdrrec_endofrecord(xdrs, TRUE);
367181344Sdfr			release_fd_lock(ct->ct_fd, mask);
368181344Sdfr			return (ct->ct_error.re_status);
369181344Sdfr		}
370181344Sdfr	} else {
371181344Sdfr		*(uint32_t *) &ct->ct_u.ct_mcallc[ct->ct_mpos] = htonl(proc);
372181344Sdfr		if (! __rpc_gss_wrap(cl->cl_auth, ct->ct_u.ct_mcallc,
373181344Sdfr			ct->ct_mpos + sizeof(uint32_t),
374181344Sdfr			xdrs, xdr_args, args_ptr)) {
375181344Sdfr			if (ct->ct_error.re_status == RPC_SUCCESS)
376181344Sdfr				ct->ct_error.re_status = RPC_CANTENCODEARGS;
377181344Sdfr			(void)xdrrec_endofrecord(xdrs, TRUE);
378181344Sdfr			release_fd_lock(ct->ct_fd, mask);
379181344Sdfr			return (ct->ct_error.re_status);
380181344Sdfr		}
38174462Salfred	}
38274462Salfred	if (! xdrrec_endofrecord(xdrs, shipnow)) {
38374462Salfred		release_fd_lock(ct->ct_fd, mask);
38474462Salfred		return (ct->ct_error.re_status = RPC_CANTSEND);
38574462Salfred	}
38674462Salfred	if (! shipnow) {
38774462Salfred		release_fd_lock(ct->ct_fd, mask);
38874462Salfred		return (RPC_SUCCESS);
38974462Salfred	}
39074462Salfred	/*
39174462Salfred	 * Hack to provide rpc-based message passing
39274462Salfred	 */
39374462Salfred	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
39474462Salfred		release_fd_lock(ct->ct_fd, mask);
39574462Salfred		return(ct->ct_error.re_status = RPC_TIMEDOUT);
39674462Salfred	}
39774462Salfred
39874462Salfred
39974462Salfred	/*
40074462Salfred	 * Keep receiving until we get a valid transaction id
40174462Salfred	 */
40274462Salfred	xdrs->x_op = XDR_DECODE;
40374462Salfred	while (TRUE) {
40474462Salfred		reply_msg.acpted_rply.ar_verf = _null_auth;
40574462Salfred		reply_msg.acpted_rply.ar_results.where = NULL;
40674462Salfred		reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
40774462Salfred		if (! xdrrec_skiprecord(xdrs)) {
40874462Salfred			release_fd_lock(ct->ct_fd, mask);
40974462Salfred			return (ct->ct_error.re_status);
41074462Salfred		}
41174462Salfred		/* now decode and validate the response header */
41274462Salfred		if (! xdr_replymsg(xdrs, &reply_msg)) {
41374462Salfred			if (ct->ct_error.re_status == RPC_SUCCESS)
41474462Salfred				continue;
41574462Salfred			release_fd_lock(ct->ct_fd, mask);
41674462Salfred			return (ct->ct_error.re_status);
41774462Salfred		}
41874462Salfred		if (reply_msg.rm_xid == x_id)
41974462Salfred			break;
42074462Salfred	}
42174462Salfred
42274462Salfred	/*
42374462Salfred	 * process header
42474462Salfred	 */
42574462Salfred	_seterr_reply(&reply_msg, &(ct->ct_error));
42674462Salfred	if (ct->ct_error.re_status == RPC_SUCCESS) {
42774462Salfred		if (! AUTH_VALIDATE(cl->cl_auth,
42874462Salfred		    &reply_msg.acpted_rply.ar_verf)) {
42974462Salfred			ct->ct_error.re_status = RPC_AUTHERROR;
43074462Salfred			ct->ct_error.re_why = AUTH_INVALIDRESP;
431181344Sdfr		} else {
432181344Sdfr			if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
433181344Sdfr				reply_stat = (*xdr_results)(xdrs, results_ptr);
434181344Sdfr			} else {
435181344Sdfr				reply_stat = __rpc_gss_unwrap(cl->cl_auth,
436181344Sdfr				    xdrs, xdr_results, results_ptr);
437181344Sdfr			}
438181344Sdfr			if (! reply_stat) {
439181344Sdfr				if (ct->ct_error.re_status == RPC_SUCCESS)
440181344Sdfr					ct->ct_error.re_status =
441181344Sdfr						RPC_CANTDECODERES;
442181344Sdfr			}
44374462Salfred		}
44474462Salfred		/* free verifier ... */
44574462Salfred		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
44674462Salfred			xdrs->x_op = XDR_FREE;
44774462Salfred			(void)xdr_opaque_auth(xdrs,
44874462Salfred			    &(reply_msg.acpted_rply.ar_verf));
44974462Salfred		}
45074462Salfred	}  /* end successful completion */
45174462Salfred	else {
45274462Salfred		/* maybe our credentials need to be refreshed ... */
45374462Salfred		if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg))
45474462Salfred			goto call_again;
45574462Salfred	}  /* end of unsuccessful completion */
45674462Salfred	release_fd_lock(ct->ct_fd, mask);
45774462Salfred	return (ct->ct_error.re_status);
45874462Salfred}
45974462Salfred
46074462Salfredstatic void
461309487Sngieclnt_vc_geterr(CLIENT *cl, struct rpc_err *errp)
46274462Salfred{
46374462Salfred	struct ct_data *ct;
46474462Salfred
46574462Salfred	assert(cl != NULL);
46674462Salfred	assert(errp != NULL);
46774462Salfred
46874462Salfred	ct = (struct ct_data *) cl->cl_private;
46974462Salfred	*errp = ct->ct_error;
47074462Salfred}
47174462Salfred
47274462Salfredstatic bool_t
473309487Sngieclnt_vc_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
47474462Salfred{
47574462Salfred	struct ct_data *ct;
47674462Salfred	XDR *xdrs;
47774462Salfred	bool_t dummy;
47874462Salfred	sigset_t mask;
47974462Salfred	sigset_t newmask;
48074462Salfred
48174462Salfred	assert(cl != NULL);
48274462Salfred
48374462Salfred	ct = (struct ct_data *)cl->cl_private;
48474462Salfred	xdrs = &(ct->ct_xdrs);
48574462Salfred
48674462Salfred	sigfillset(&newmask);
48774462Salfred	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
48874462Salfred	mutex_lock(&clnt_fd_lock);
48974462Salfred	while (vc_fd_locks[ct->ct_fd])
49074462Salfred		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
49174462Salfred	xdrs->x_op = XDR_FREE;
49274462Salfred	dummy = (*xdr_res)(xdrs, res_ptr);
49374462Salfred	mutex_unlock(&clnt_fd_lock);
49474462Salfred	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
49574462Salfred	cond_signal(&vc_cv[ct->ct_fd]);
49674462Salfred
49774462Salfred	return dummy;
49874462Salfred}
49974462Salfred
50074462Salfred/*ARGSUSED*/
50174462Salfredstatic void
502309487Sngieclnt_vc_abort(CLIENT *cl)
50374462Salfred{
50474462Salfred}
50574462Salfred
506309498Sngiestatic __inline void
507309498Sngiehtonlp(void *dst, const void *src, uint32_t incr)
508309498Sngie{
509309498Sngie	/* We are aligned, so we think */
510309498Sngie	*(uint32_t *)dst = htonl(*(const uint32_t *)src + incr);
511309498Sngie}
512309498Sngie
513309498Sngiestatic __inline void
514309498Sngientohlp(void *dst, const void *src)
515309498Sngie{
516309498Sngie	/* We are aligned, so we think */
517309498Sngie	*(uint32_t *)dst = htonl(*(const uint32_t *)src);
518309498Sngie}
519309498Sngie
52074462Salfredstatic bool_t
521309487Sngieclnt_vc_control(CLIENT *cl, u_int request, void *info)
52274462Salfred{
52374462Salfred	struct ct_data *ct;
52474462Salfred	void *infop = info;
52574462Salfred	sigset_t mask;
52674462Salfred	sigset_t newmask;
52774462Salfred	int rpc_lock_value;
52874462Salfred
52974462Salfred	assert(cl != NULL);
53074462Salfred
53174462Salfred	ct = (struct ct_data *)cl->cl_private;
53274462Salfred
53374462Salfred	sigfillset(&newmask);
53474462Salfred	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
53574462Salfred	mutex_lock(&clnt_fd_lock);
53674462Salfred	while (vc_fd_locks[ct->ct_fd])
53774462Salfred		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
53874462Salfred	if (__isthreaded)
53974462Salfred                rpc_lock_value = 1;
54074462Salfred        else
54174462Salfred                rpc_lock_value = 0;
54274462Salfred	vc_fd_locks[ct->ct_fd] = rpc_lock_value;
54374462Salfred	mutex_unlock(&clnt_fd_lock);
54474462Salfred
54574462Salfred	switch (request) {
54674462Salfred	case CLSET_FD_CLOSE:
54774462Salfred		ct->ct_closeit = TRUE;
54874462Salfred		release_fd_lock(ct->ct_fd, mask);
54974462Salfred		return (TRUE);
55074462Salfred	case CLSET_FD_NCLOSE:
55174462Salfred		ct->ct_closeit = FALSE;
55274462Salfred		release_fd_lock(ct->ct_fd, mask);
55374462Salfred		return (TRUE);
55474462Salfred	default:
55574462Salfred		break;
55674462Salfred	}
55774462Salfred
55874462Salfred	/* for other requests which use info */
55974462Salfred	if (info == NULL) {
56074462Salfred		release_fd_lock(ct->ct_fd, mask);
56174462Salfred		return (FALSE);
56274462Salfred	}
56374462Salfred	switch (request) {
56474462Salfred	case CLSET_TIMEOUT:
56599996Salfred		if (time_not_ok((struct timeval *)info)) {
56674462Salfred			release_fd_lock(ct->ct_fd, mask);
56774462Salfred			return (FALSE);
56874462Salfred		}
56974462Salfred		ct->ct_wait = *(struct timeval *)infop;
57074462Salfred		ct->ct_waitset = TRUE;
57174462Salfred		break;
57274462Salfred	case CLGET_TIMEOUT:
57374462Salfred		*(struct timeval *)infop = ct->ct_wait;
57474462Salfred		break;
57574462Salfred	case CLGET_SERVER_ADDR:
57674462Salfred		(void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
57774462Salfred		break;
57874462Salfred	case CLGET_FD:
57999996Salfred		*(int *)info = ct->ct_fd;
58074462Salfred		break;
58174462Salfred	case CLGET_SVC_ADDR:
58274462Salfred		/* The caller should not free this memory area */
58399996Salfred		*(struct netbuf *)info = ct->ct_addr;
58474462Salfred		break;
58574462Salfred	case CLSET_SVC_ADDR:		/* set to new address */
58674462Salfred		release_fd_lock(ct->ct_fd, mask);
58774462Salfred		return (FALSE);
58874462Salfred	case CLGET_XID:
58974462Salfred		/*
59074462Salfred		 * use the knowledge that xid is the
59174462Salfred		 * first element in the call structure
59274462Salfred		 * This will get the xid of the PREVIOUS call
59374462Salfred		 */
594309498Sngie		ntohlp(info, &ct->ct_u.ct_mcalli);
59574462Salfred		break;
59674462Salfred	case CLSET_XID:
59774462Salfred		/* This will set the xid of the NEXT call */
59874462Salfred		/* increment by 1 as clnt_vc_call() decrements once */
599309498Sngie		htonlp(&ct->ct_u.ct_mcalli, info, 1);
60074462Salfred		break;
60174462Salfred	case CLGET_VERS:
60274462Salfred		/*
60374462Salfred		 * This RELIES on the information that, in the call body,
60474462Salfred		 * the version number field is the fifth field from the
60574462Salfred		 * begining of the RPC header. MUST be changed if the
60674462Salfred		 * call_struct is changed
60774462Salfred		 */
608309498Sngie		ntohlp(info, ct->ct_u.ct_mcallc + 4 * BYTES_PER_XDR_UNIT);
60974462Salfred		break;
61074462Salfred
61174462Salfred	case CLSET_VERS:
612309498Sngie		htonlp(ct->ct_u.ct_mcallc + 4 * BYTES_PER_XDR_UNIT, info, 0);
61374462Salfred		break;
61474462Salfred
61574462Salfred	case CLGET_PROG:
61674462Salfred		/*
61774462Salfred		 * This RELIES on the information that, in the call body,
61874462Salfred		 * the program number field is the fourth field from the
61974462Salfred		 * begining of the RPC header. MUST be changed if the
62074462Salfred		 * call_struct is changed
62174462Salfred		 */
622309498Sngie		ntohlp(info, ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT);
62374462Salfred		break;
62474462Salfred
62574462Salfred	case CLSET_PROG:
626309498Sngie		htonlp(ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT, info, 0);
62774462Salfred		break;
62874462Salfred
62974462Salfred	default:
63074462Salfred		release_fd_lock(ct->ct_fd, mask);
63174462Salfred		return (FALSE);
63274462Salfred	}
63374462Salfred	release_fd_lock(ct->ct_fd, mask);
63474462Salfred	return (TRUE);
63574462Salfred}
63674462Salfred
63774462Salfred
63874462Salfredstatic void
639309487Sngieclnt_vc_destroy(CLIENT *cl)
64074462Salfred{
64174462Salfred	struct ct_data *ct = (struct ct_data *) cl->cl_private;
64274462Salfred	int ct_fd = ct->ct_fd;
64374462Salfred	sigset_t mask;
64474462Salfred	sigset_t newmask;
64574462Salfred
64674462Salfred	assert(cl != NULL);
64774462Salfred
64874462Salfred	ct = (struct ct_data *) cl->cl_private;
64974462Salfred
65074462Salfred	sigfillset(&newmask);
65174462Salfred	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
65274462Salfred	mutex_lock(&clnt_fd_lock);
65374462Salfred	while (vc_fd_locks[ct_fd])
65474462Salfred		cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
65574462Salfred	if (ct->ct_closeit && ct->ct_fd != -1) {
65674462Salfred		(void)_close(ct->ct_fd);
65774462Salfred	}
65874462Salfred	XDR_DESTROY(&(ct->ct_xdrs));
659290899Sngie	free(ct->ct_addr.buf);
66074462Salfred	mem_free(ct, sizeof(struct ct_data));
661241143Spfg	if (cl->cl_netid && cl->cl_netid[0])
662241143Spfg		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
663241143Spfg	if (cl->cl_tp && cl->cl_tp[0])
664241143Spfg		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
66574462Salfred	mem_free(cl, sizeof(CLIENT));
66674462Salfred	mutex_unlock(&clnt_fd_lock);
66774462Salfred	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
66874462Salfred	cond_signal(&vc_cv[ct_fd]);
66974462Salfred}
67074462Salfred
67174462Salfred/*
67274462Salfred * Interface between xdr serializer and tcp connection.
67374462Salfred * Behaves like the system calls, read & write, but keeps some error state
67474462Salfred * around for the rpc level.
67574462Salfred */
67674462Salfredstatic int
677309487Sngieread_vc(void *ctp, void *buf, int len)
67874462Salfred{
67974462Salfred	struct sockaddr sa;
68074462Salfred	socklen_t sal;
68195658Sdes	struct ct_data *ct = (struct ct_data *)ctp;
68274462Salfred	struct pollfd fd;
68374462Salfred	int milliseconds = (int)((ct->ct_wait.tv_sec * 1000) +
68474462Salfred	    (ct->ct_wait.tv_usec / 1000));
68574462Salfred
68674462Salfred	if (len == 0)
68774462Salfred		return (0);
68874462Salfred	fd.fd = ct->ct_fd;
68974462Salfred	fd.events = POLLIN;
69074462Salfred	for (;;) {
69174462Salfred		switch (_poll(&fd, 1, milliseconds)) {
69274462Salfred		case 0:
69374462Salfred			ct->ct_error.re_status = RPC_TIMEDOUT;
69474462Salfred			return (-1);
69574462Salfred
69674462Salfred		case -1:
69774462Salfred			if (errno == EINTR)
69874462Salfred				continue;
69974462Salfred			ct->ct_error.re_status = RPC_CANTRECV;
70074462Salfred			ct->ct_error.re_errno = errno;
70174462Salfred			return (-1);
70274462Salfred		}
70374462Salfred		break;
70474462Salfred	}
70574462Salfred
70674462Salfred	sal = sizeof(sa);
70774462Salfred	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
70874462Salfred	    (sa.sa_family == AF_LOCAL)) {
70974462Salfred		len = __msgread(ct->ct_fd, buf, (size_t)len);
71074462Salfred	} else {
71174462Salfred		len = _read(ct->ct_fd, buf, (size_t)len);
71274462Salfred	}
71374462Salfred
71474462Salfred	switch (len) {
71574462Salfred	case 0:
71674462Salfred		/* premature eof */
71774462Salfred		ct->ct_error.re_errno = ECONNRESET;
71874462Salfred		ct->ct_error.re_status = RPC_CANTRECV;
71974462Salfred		len = -1;  /* it's really an error */
72074462Salfred		break;
72174462Salfred
72274462Salfred	case -1:
72374462Salfred		ct->ct_error.re_errno = errno;
72474462Salfred		ct->ct_error.re_status = RPC_CANTRECV;
72574462Salfred		break;
72674462Salfred	}
72774462Salfred	return (len);
72874462Salfred}
72974462Salfred
73074462Salfredstatic int
731309487Sngiewrite_vc(void *ctp, void *buf, int len)
73274462Salfred{
73374462Salfred	struct sockaddr sa;
73474462Salfred	socklen_t sal;
73595658Sdes	struct ct_data *ct = (struct ct_data *)ctp;
73674462Salfred	int i, cnt;
73774462Salfred
73874462Salfred	sal = sizeof(sa);
73974462Salfred	if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) &&
74074462Salfred	    (sa.sa_family == AF_LOCAL)) {
741143347Sstefanf		for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
74274462Salfred			if ((i = __msgwrite(ct->ct_fd, buf,
74374462Salfred			     (size_t)cnt)) == -1) {
74474462Salfred				ct->ct_error.re_errno = errno;
74574462Salfred				ct->ct_error.re_status = RPC_CANTSEND;
74674462Salfred				return (-1);
74774462Salfred			}
74874462Salfred		}
74974462Salfred	} else {
750133693Sstefanf		for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
75174462Salfred			if ((i = _write(ct->ct_fd, buf, (size_t)cnt)) == -1) {
75274462Salfred				ct->ct_error.re_errno = errno;
75374462Salfred				ct->ct_error.re_status = RPC_CANTSEND;
75474462Salfred				return (-1);
75574462Salfred			}
75674462Salfred		}
75774462Salfred	}
75874462Salfred	return (len);
75974462Salfred}
76074462Salfred
76174462Salfredstatic struct clnt_ops *
762309487Sngieclnt_vc_ops(void)
76374462Salfred{
76474462Salfred	static struct clnt_ops ops;
76574462Salfred	sigset_t mask, newmask;
76674462Salfred
76774462Salfred	/* VARIABLES PROTECTED BY ops_lock: ops */
76874462Salfred
76974462Salfred	sigfillset(&newmask);
77074462Salfred	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
77174462Salfred	mutex_lock(&ops_lock);
77274462Salfred	if (ops.cl_call == NULL) {
77374462Salfred		ops.cl_call = clnt_vc_call;
77474462Salfred		ops.cl_abort = clnt_vc_abort;
77574462Salfred		ops.cl_geterr = clnt_vc_geterr;
77674462Salfred		ops.cl_freeres = clnt_vc_freeres;
77774462Salfred		ops.cl_destroy = clnt_vc_destroy;
77874462Salfred		ops.cl_control = clnt_vc_control;
77974462Salfred	}
78074462Salfred	mutex_unlock(&ops_lock);
78174462Salfred	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
78274462Salfred	return (&ops);
78374462Salfred}
78474462Salfred
78574462Salfred/*
78674462Salfred * Make sure that the time is not garbage.   -1 value is disallowed.
78774462Salfred * Note this is different from time_not_ok in clnt_dg.c
78874462Salfred */
78974462Salfredstatic bool_t
790309487Sngietime_not_ok(struct timeval *t)
79174462Salfred{
79274462Salfred	return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
79374462Salfred		t->tv_usec <= -1 || t->tv_usec > 1000000);
79474462Salfred}
79574462Salfred
79674627Salfredstatic int
797309487Sngie__msgread(int sock, void *buf, size_t cnt)
79874462Salfred{
79974462Salfred	struct iovec iov[1];
80074462Salfred	struct msghdr msg;
80184472Sdwmalone	union {
80284472Sdwmalone		struct cmsghdr cmsg;
80384472Sdwmalone		char control[CMSG_SPACE(sizeof(struct cmsgcred))];
80484472Sdwmalone	} cm;
80574462Salfred
80674462Salfred	bzero((char *)&cm, sizeof(cm));
80774462Salfred	iov[0].iov_base = buf;
80874462Salfred	iov[0].iov_len = cnt;
80974462Salfred
81074462Salfred	msg.msg_iov = iov;
81174462Salfred	msg.msg_iovlen = 1;
81274462Salfred	msg.msg_name = NULL;
81374462Salfred	msg.msg_namelen = 0;
81474462Salfred	msg.msg_control = (caddr_t)&cm;
81584472Sdwmalone	msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
81674462Salfred	msg.msg_flags = 0;
81774462Salfred
81874462Salfred	return(_recvmsg(sock, &msg, 0));
81974462Salfred}
82074627Salfred
82174462Salfredstatic int
822309487Sngie__msgwrite(int sock, void *buf, size_t cnt)
82374462Salfred{
82474462Salfred	struct iovec iov[1];
82574462Salfred	struct msghdr msg;
82684472Sdwmalone	union {
82784472Sdwmalone		struct cmsghdr cmsg;
82884472Sdwmalone		char control[CMSG_SPACE(sizeof(struct cmsgcred))];
82984472Sdwmalone	} cm;
83074462Salfred
83174462Salfred	bzero((char *)&cm, sizeof(cm));
83274462Salfred	iov[0].iov_base = buf;
83374462Salfred	iov[0].iov_len = cnt;
83474462Salfred
83574462Salfred	cm.cmsg.cmsg_type = SCM_CREDS;
83674462Salfred	cm.cmsg.cmsg_level = SOL_SOCKET;
83784472Sdwmalone	cm.cmsg.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
83874462Salfred
83974462Salfred	msg.msg_iov = iov;
84074462Salfred	msg.msg_iovlen = 1;
84174462Salfred	msg.msg_name = NULL;
84274462Salfred	msg.msg_namelen = 0;
84374462Salfred	msg.msg_control = (caddr_t)&cm;
84484472Sdwmalone	msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
84574462Salfred	msg.msg_flags = 0;
84674462Salfred
84774462Salfred	return(_sendmsg(sock, &msg, 0));
84874462Salfred}
849