svc_vc.c revision 116391
174462Salfred/*	$NetBSD: svc_vc.c,v 1.7 2000/08/03 00:01:53 fvdl Exp $	*/
274462Salfred
374462Salfred/*
474462Salfred * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
574462Salfred * unrestricted use provided that this legend is included on all tape
674462Salfred * media and as a part of the software program in whole or part.  Users
774462Salfred * may copy or modify Sun RPC without charge, but are not authorized
874462Salfred * to license or distribute it to anyone else except as part of a product or
974462Salfred * program developed by the user.
1074462Salfred *
1174462Salfred * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
1274462Salfred * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
1374462Salfred * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
1474462Salfred *
1574462Salfred * Sun RPC is provided with no support and without any obligation on the
1674462Salfred * part of Sun Microsystems, Inc. to assist in its use, correction,
1774462Salfred * modification or enhancement.
1874462Salfred *
1974462Salfred * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
2074462Salfred * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
2174462Salfred * OR ANY PART THEREOF.
2274462Salfred *
2374462Salfred * In no event will Sun Microsystems, Inc. be liable for any lost revenue
2474462Salfred * or profits or other special, indirect and consequential damages, even if
2574462Salfred * Sun has been advised of the possibility of such damages.
2674462Salfred *
2774462Salfred * Sun Microsystems, Inc.
2874462Salfred * 2550 Garcia Avenue
2974462Salfred * Mountain View, California  94043
3074462Salfred */
3174462Salfred
3274462Salfred#if defined(LIBC_SCCS) && !defined(lint)
3374462Salfredstatic char *sccsid = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
3474462Salfredstatic char *sccsid = "@(#)svc_tcp.c	2.2 88/08/01 4.0 RPCSRC";
3574462Salfred#endif
3692990Sobrien#include <sys/cdefs.h>
3792990Sobrien__FBSDID("$FreeBSD: head/lib/libc/rpc/svc_vc.c 116391 2003-06-15 10:32:01Z mbr $");
3874462Salfred
3974462Salfred/*
4074462Salfred * svc_vc.c, Server side for Connection Oriented based RPC.
4174462Salfred *
4274462Salfred * Actually implements two flavors of transporter -
4374462Salfred * a tcp rendezvouser (a listner and connection establisher)
4474462Salfred * and a record/tcp stream.
4574462Salfred */
4674462Salfred
4775094Siedowse#include "namespace.h"
4874462Salfred#include "reentrant.h"
4974462Salfred#include <sys/types.h>
5074462Salfred#include <sys/param.h>
5174462Salfred#include <sys/poll.h>
5274462Salfred#include <sys/socket.h>
5374462Salfred#include <sys/un.h>
54109359Smbr#include <sys/time.h>
5574462Salfred#include <sys/uio.h>
5674462Salfred#include <netinet/in.h>
5774462Salfred#include <netinet/tcp.h>
5874462Salfred
5974462Salfred#include <assert.h>
6074462Salfred#include <err.h>
6174462Salfred#include <errno.h>
62109359Smbr#include <fcntl.h>
6374462Salfred#include <stdio.h>
6474462Salfred#include <stdlib.h>
6574462Salfred#include <string.h>
6674462Salfred#include <unistd.h>
6774462Salfred
6874462Salfred#include <rpc/rpc.h>
6974462Salfred
7074462Salfred#include "rpc_com.h"
7174462Salfred#include "un-namespace.h"
7274462Salfred
73109359Smbrextern rwlock_t svc_fd_lock;
74109359Smbr
7592905Sobrienstatic SVCXPRT *makefd_xprt(int, u_int, u_int);
7692905Sobrienstatic bool_t rendezvous_request(SVCXPRT *, struct rpc_msg *);
7792905Sobrienstatic enum xprt_stat rendezvous_stat(SVCXPRT *);
7892905Sobrienstatic void svc_vc_destroy(SVCXPRT *);
79109359Smbrstatic void __svc_vc_dodestroy (SVCXPRT *);
8095658Sdesstatic int read_vc(void *, void *, int);
8195658Sdesstatic int write_vc(void *, void *, int);
8292905Sobrienstatic enum xprt_stat svc_vc_stat(SVCXPRT *);
8392905Sobrienstatic bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *);
8495658Sdesstatic bool_t svc_vc_getargs(SVCXPRT *, xdrproc_t, void *);
8595658Sdesstatic bool_t svc_vc_freeargs(SVCXPRT *, xdrproc_t, void *);
8692905Sobrienstatic bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *);
8792905Sobrienstatic void svc_vc_rendezvous_ops(SVCXPRT *);
8892905Sobrienstatic void svc_vc_ops(SVCXPRT *);
8992905Sobrienstatic bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in);
90109359Smbrstatic bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq,
91109359Smbr				   	     void *in);
9274462Salfredstatic int __msgwrite(int, void *, size_t);
9374462Salfred
9474462Salfredstruct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */
9574462Salfred	u_int sendsize;
9674462Salfred	u_int recvsize;
97109359Smbr	int maxrec;
9874462Salfred};
9974462Salfred
10074462Salfredstruct cf_conn {  /* kept in xprt->xp_p1 for actual connection */
10174462Salfred	enum xprt_stat strm_stat;
10274462Salfred	u_int32_t x_id;
10374462Salfred	XDR xdrs;
10474462Salfred	char verf_body[MAX_AUTH_BYTES];
105109359Smbr	u_int sendsize;
106109359Smbr	u_int recvsize;
107109359Smbr	int maxrec;
108109359Smbr	bool_t nonblock;
109109359Smbr	struct timeval last_recv_time;
11074462Salfred};
11174462Salfred
11274462Salfred/*
11374462Salfred * Usage:
11474462Salfred *	xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
11574462Salfred *
11674462Salfred * Creates, registers, and returns a (rpc) tcp based transporter.
11774462Salfred * Once *xprt is initialized, it is registered as a transporter
11874462Salfred * see (svc.h, xprt_register).  This routine returns
11974462Salfred * a NULL if a problem occurred.
12074462Salfred *
12174462Salfred * The filedescriptor passed in is expected to refer to a bound, but
12274462Salfred * not yet connected socket.
12374462Salfred *
12474462Salfred * Since streams do buffered io similar to stdio, the caller can specify
12574462Salfred * how big the send and receive buffers are via the second and third parms;
12674462Salfred * 0 => use the system default.
12774462Salfred */
12874462SalfredSVCXPRT *
12974462Salfredsvc_vc_create(fd, sendsize, recvsize)
13074462Salfred	int fd;
13174462Salfred	u_int sendsize;
13274462Salfred	u_int recvsize;
13374462Salfred{
13474462Salfred	SVCXPRT *xprt;
13574462Salfred	struct cf_rendezvous *r = NULL;
13674462Salfred	struct __rpc_sockinfo si;
13774462Salfred	struct sockaddr_storage sslocal;
13874462Salfred	socklen_t slen;
13974462Salfred
14074462Salfred	r = mem_alloc(sizeof(*r));
14174462Salfred	if (r == NULL) {
14274462Salfred		warnx("svc_vc_create: out of memory");
14374462Salfred		goto cleanup_svc_vc_create;
14474462Salfred	}
14574462Salfred	if (!__rpc_fd2sockinfo(fd, &si))
14674462Salfred		return NULL;
14774462Salfred	r->sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
14874462Salfred	r->recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
149109359Smbr	r->maxrec = __svc_maxrec;
15074462Salfred	xprt = mem_alloc(sizeof(SVCXPRT));
15174462Salfred	if (xprt == NULL) {
15274462Salfred		warnx("svc_vc_create: out of memory");
15374462Salfred		goto cleanup_svc_vc_create;
15474462Salfred	}
15574462Salfred	xprt->xp_tp = NULL;
15695658Sdes	xprt->xp_p1 = r;
15774462Salfred	xprt->xp_p2 = NULL;
15874462Salfred	xprt->xp_p3 = NULL;
15974462Salfred	xprt->xp_verf = _null_auth;
16074462Salfred	svc_vc_rendezvous_ops(xprt);
16174462Salfred	xprt->xp_port = (u_short)-1;	/* It is the rendezvouser */
16274462Salfred	xprt->xp_fd = fd;
16374462Salfred
16474462Salfred	slen = sizeof (struct sockaddr_storage);
16574462Salfred	if (_getsockname(fd, (struct sockaddr *)(void *)&sslocal, &slen) < 0) {
16674462Salfred		warnx("svc_vc_create: could not retrieve local addr");
16774462Salfred		goto cleanup_svc_vc_create;
16874462Salfred	}
16974462Salfred
17074462Salfred	xprt->xp_ltaddr.maxlen = xprt->xp_ltaddr.len = sslocal.ss_len;
17174462Salfred	xprt->xp_ltaddr.buf = mem_alloc((size_t)sslocal.ss_len);
17274462Salfred	if (xprt->xp_ltaddr.buf == NULL) {
17374462Salfred		warnx("svc_vc_create: no mem for local addr");
17474462Salfred		goto cleanup_svc_vc_create;
17574462Salfred	}
17674462Salfred	memcpy(xprt->xp_ltaddr.buf, &sslocal, (size_t)sslocal.ss_len);
17774462Salfred
17874462Salfred	xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
17974462Salfred	xprt_register(xprt);
18074462Salfred	return (xprt);
18174462Salfredcleanup_svc_vc_create:
18274462Salfred	if (r != NULL)
18374462Salfred		mem_free(r, sizeof(*r));
18474462Salfred	return (NULL);
18574462Salfred}
18674462Salfred
18774462Salfred/*
18874462Salfred * Like svtcp_create(), except the routine takes any *open* UNIX file
18974462Salfred * descriptor as its first input.
19074462Salfred */
19174462SalfredSVCXPRT *
19274462Salfredsvc_fd_create(fd, sendsize, recvsize)
19374462Salfred	int fd;
19474462Salfred	u_int sendsize;
19574462Salfred	u_int recvsize;
19674462Salfred{
19774462Salfred	struct sockaddr_storage ss;
19874462Salfred	socklen_t slen;
19974462Salfred	SVCXPRT *ret;
20074462Salfred
20174462Salfred	assert(fd != -1);
20274462Salfred
20374462Salfred	ret = makefd_xprt(fd, sendsize, recvsize);
20474462Salfred	if (ret == NULL)
20574462Salfred		return NULL;
20674462Salfred
20774462Salfred	slen = sizeof (struct sockaddr_storage);
20874462Salfred	if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
20974462Salfred		warnx("svc_fd_create: could not retrieve local addr");
21074462Salfred		goto freedata;
21174462Salfred	}
21274462Salfred	ret->xp_ltaddr.maxlen = ret->xp_ltaddr.len = ss.ss_len;
21374462Salfred	ret->xp_ltaddr.buf = mem_alloc((size_t)ss.ss_len);
21474462Salfred	if (ret->xp_ltaddr.buf == NULL) {
21574462Salfred		warnx("svc_fd_create: no mem for local addr");
21674462Salfred		goto freedata;
21774462Salfred	}
21874462Salfred	memcpy(ret->xp_ltaddr.buf, &ss, (size_t)ss.ss_len);
21974462Salfred
22074462Salfred	slen = sizeof (struct sockaddr_storage);
22174462Salfred	if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
22274462Salfred		warnx("svc_fd_create: could not retrieve remote addr");
22374462Salfred		goto freedata;
22474462Salfred	}
22574462Salfred	ret->xp_rtaddr.maxlen = ret->xp_rtaddr.len = ss.ss_len;
22674462Salfred	ret->xp_rtaddr.buf = mem_alloc((size_t)ss.ss_len);
22774462Salfred	if (ret->xp_rtaddr.buf == NULL) {
22874462Salfred		warnx("svc_fd_create: no mem for local addr");
22974462Salfred		goto freedata;
23074462Salfred	}
23174462Salfred	memcpy(ret->xp_rtaddr.buf, &ss, (size_t)ss.ss_len);
23274462Salfred#ifdef PORTMAP
23390272Salfred	if (ss.ss_family == AF_INET || ss.ss_family == AF_LOCAL) {
23474462Salfred		ret->xp_raddr = *(struct sockaddr_in *)ret->xp_rtaddr.buf;
23574462Salfred		ret->xp_addrlen = sizeof (struct sockaddr_in);
23674462Salfred	}
23774462Salfred#endif				/* PORTMAP */
23874462Salfred
23974462Salfred	return ret;
24074462Salfred
24174462Salfredfreedata:
24274462Salfred	if (ret->xp_ltaddr.buf != NULL)
24374462Salfred		mem_free(ret->xp_ltaddr.buf, rep->xp_ltaddr.maxlen);
24474462Salfred
24574462Salfred	return NULL;
24674462Salfred}
24774462Salfred
24874462Salfredstatic SVCXPRT *
24974462Salfredmakefd_xprt(fd, sendsize, recvsize)
25074462Salfred	int fd;
25174462Salfred	u_int sendsize;
25274462Salfred	u_int recvsize;
25374462Salfred{
25474462Salfred	SVCXPRT *xprt;
25574462Salfred	struct cf_conn *cd;
25674462Salfred	const char *netid;
25774462Salfred	struct __rpc_sockinfo si;
25874462Salfred
25974462Salfred	assert(fd != -1);
26074462Salfred
26174462Salfred	xprt = mem_alloc(sizeof(SVCXPRT));
26274462Salfred	if (xprt == NULL) {
26374462Salfred		warnx("svc_vc: makefd_xprt: out of memory");
26474462Salfred		goto done;
26574462Salfred	}
26674462Salfred	memset(xprt, 0, sizeof *xprt);
26774462Salfred	cd = mem_alloc(sizeof(struct cf_conn));
26874462Salfred	if (cd == NULL) {
26974462Salfred		warnx("svc_tcp: makefd_xprt: out of memory");
27074462Salfred		mem_free(xprt, sizeof(SVCXPRT));
27174462Salfred		xprt = NULL;
27274462Salfred		goto done;
27374462Salfred	}
27474462Salfred	cd->strm_stat = XPRT_IDLE;
27574462Salfred	xdrrec_create(&(cd->xdrs), sendsize, recvsize,
27695658Sdes	    xprt, read_vc, write_vc);
27795658Sdes	xprt->xp_p1 = cd;
27874462Salfred	xprt->xp_verf.oa_base = cd->verf_body;
27974462Salfred	svc_vc_ops(xprt);  /* truely deals with calls */
28074462Salfred	xprt->xp_port = 0;  /* this is a connection, not a rendezvouser */
28174462Salfred	xprt->xp_fd = fd;
28274462Salfred        if (__rpc_fd2sockinfo(fd, &si) && __rpc_sockinfo2netid(&si, &netid))
28374462Salfred		xprt->xp_netid = strdup(netid);
28474462Salfred
28574462Salfred	xprt_register(xprt);
28674462Salfreddone:
28774462Salfred	return (xprt);
28874462Salfred}
28974462Salfred
29074462Salfred/*ARGSUSED*/
29174462Salfredstatic bool_t
29274462Salfredrendezvous_request(xprt, msg)
29374462Salfred	SVCXPRT *xprt;
29474462Salfred	struct rpc_msg *msg;
29574462Salfred{
296109359Smbr	int sock, flags;
29774462Salfred	struct cf_rendezvous *r;
298109359Smbr	struct cf_conn *cd;
29974462Salfred	struct sockaddr_storage addr;
30074462Salfred	socklen_t len;
30174462Salfred	struct __rpc_sockinfo si;
302109359Smbr	SVCXPRT *newxprt;
303109359Smbr	fd_set cleanfds;
30474462Salfred
30574462Salfred	assert(xprt != NULL);
30674462Salfred	assert(msg != NULL);
30774462Salfred
30874462Salfred	r = (struct cf_rendezvous *)xprt->xp_p1;
30974462Salfredagain:
31074462Salfred	len = sizeof addr;
31174462Salfred	if ((sock = _accept(xprt->xp_fd, (struct sockaddr *)(void *)&addr,
31274462Salfred	    &len)) < 0) {
31374462Salfred		if (errno == EINTR)
31474462Salfred			goto again;
315109359Smbr		/*
316109359Smbr		 * Clean out the most idle file descriptor when we're
317109359Smbr		 * running out.
318109359Smbr		 */
319109359Smbr		if (errno == EMFILE || errno == ENFILE) {
320109359Smbr			cleanfds = svc_fdset;
321109359Smbr			__svc_clean_idle(&cleanfds, 0, FALSE);
322109359Smbr			goto again;
323109359Smbr		}
324109359Smbr		return (FALSE);
32574462Salfred	}
32674462Salfred	/*
32774462Salfred	 * make a new transporter (re-uses xprt)
32874462Salfred	 */
329109359Smbr	newxprt = makefd_xprt(sock, r->sendsize, r->recvsize);
330109359Smbr	newxprt->xp_rtaddr.buf = mem_alloc(len);
331109359Smbr	if (newxprt->xp_rtaddr.buf == NULL)
33274462Salfred		return (FALSE);
333109359Smbr	memcpy(newxprt->xp_rtaddr.buf, &addr, len);
334109359Smbr	newxprt->xp_rtaddr.len = len;
33574462Salfred#ifdef PORTMAP
33690316Salfred	if (addr.ss_family == AF_INET || addr.ss_family == AF_LOCAL) {
337109359Smbr		newxprt->xp_raddr = *(struct sockaddr_in *)newxprt->xp_rtaddr.buf;
338109359Smbr		newxprt->xp_addrlen = sizeof (struct sockaddr_in);
33974462Salfred	}
34074462Salfred#endif				/* PORTMAP */
34174462Salfred	if (__rpc_fd2sockinfo(sock, &si) && si.si_proto == IPPROTO_TCP) {
34274462Salfred		len = 1;
34374462Salfred		/* XXX fvdl - is this useful? */
34474462Salfred		_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &len, sizeof (len));
34574462Salfred	}
346109359Smbr
347109359Smbr	cd = (struct cf_conn *)newxprt->xp_p1;
348109359Smbr
349109359Smbr	cd->recvsize = r->recvsize;
350109359Smbr	cd->sendsize = r->sendsize;
351109359Smbr	cd->maxrec = r->maxrec;
352109359Smbr
353109359Smbr	if (cd->maxrec != 0) {
354109904Smbr		flags = _fcntl(sock, F_GETFL, 0);
355109359Smbr		if (flags  == -1)
356109359Smbr			return (FALSE);
357109904Smbr		if (_fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
358109359Smbr			return (FALSE);
359109359Smbr		if (cd->recvsize > cd->maxrec)
360109359Smbr			cd->recvsize = cd->maxrec;
361109359Smbr		cd->nonblock = TRUE;
362109359Smbr		__xdrrec_setnonblock(&cd->xdrs, cd->maxrec);
363109359Smbr	} else
364109359Smbr		cd->nonblock = FALSE;
365109359Smbr
366109359Smbr	gettimeofday(&cd->last_recv_time, NULL);
367109359Smbr
36874462Salfred	return (FALSE); /* there is never an rpc msg to be processed */
36974462Salfred}
37074462Salfred
37174462Salfred/*ARGSUSED*/
37274462Salfredstatic enum xprt_stat
37374462Salfredrendezvous_stat(xprt)
37474462Salfred	SVCXPRT *xprt;
37574462Salfred{
37674462Salfred
37774462Salfred	return (XPRT_IDLE);
37874462Salfred}
37974462Salfred
38074462Salfredstatic void
38174462Salfredsvc_vc_destroy(xprt)
38274462Salfred	SVCXPRT *xprt;
38374462Salfred{
384109359Smbr	assert(xprt != NULL);
385109359Smbr
386109359Smbr	xprt_unregister(xprt);
387109359Smbr	__svc_vc_dodestroy(xprt);
388109359Smbr}
389109359Smbr
390109359Smbrstatic void
391109359Smbr__svc_vc_dodestroy(xprt)
392109359Smbr	SVCXPRT *xprt;
393109359Smbr{
39474462Salfred	struct cf_conn *cd;
39574462Salfred	struct cf_rendezvous *r;
39674462Salfred
39774462Salfred	cd = (struct cf_conn *)xprt->xp_p1;
39874462Salfred
39974462Salfred	if (xprt->xp_fd != RPC_ANYFD)
40074462Salfred		(void)_close(xprt->xp_fd);
40174462Salfred	if (xprt->xp_port != 0) {
40274462Salfred		/* a rendezvouser socket */
40374462Salfred		r = (struct cf_rendezvous *)xprt->xp_p1;
40474462Salfred		mem_free(r, sizeof (struct cf_rendezvous));
40574462Salfred		xprt->xp_port = 0;
40674462Salfred	} else {
40774462Salfred		/* an actual connection socket */
40874462Salfred		XDR_DESTROY(&(cd->xdrs));
40974462Salfred		mem_free(cd, sizeof(struct cf_conn));
41074462Salfred	}
41174462Salfred	if (xprt->xp_rtaddr.buf)
41274462Salfred		mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
41374462Salfred	if (xprt->xp_ltaddr.buf)
41474462Salfred		mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
41574462Salfred	if (xprt->xp_tp)
41674462Salfred		free(xprt->xp_tp);
41774462Salfred	if (xprt->xp_netid)
41874462Salfred		free(xprt->xp_netid);
41974462Salfred	mem_free(xprt, sizeof(SVCXPRT));
42074462Salfred}
42174462Salfred
42274462Salfred/*ARGSUSED*/
42374462Salfredstatic bool_t
42474462Salfredsvc_vc_control(xprt, rq, in)
42574462Salfred	SVCXPRT *xprt;
42674462Salfred	const u_int rq;
42774462Salfred	void *in;
42874462Salfred{
42974462Salfred	return (FALSE);
43074462Salfred}
43174462Salfred
432109359Smbrstatic bool_t
433109359Smbrsvc_vc_rendezvous_control(xprt, rq, in)
434109359Smbr	SVCXPRT *xprt;
435109359Smbr	const u_int rq;
436109359Smbr	void *in;
437109359Smbr{
438109359Smbr	struct cf_rendezvous *cfp;
439109359Smbr
440109359Smbr	cfp = (struct cf_rendezvous *)xprt->xp_p1;
441109359Smbr	if (cfp == NULL)
442109359Smbr		return (FALSE);
443109359Smbr	switch (rq) {
444109359Smbr		case SVCGET_CONNMAXREC:
445109359Smbr			*(int *)in = cfp->maxrec;
446109359Smbr			break;
447109359Smbr		case SVCSET_CONNMAXREC:
448109359Smbr			cfp->maxrec = *(int *)in;
449109359Smbr			break;
450109359Smbr		default:
451109359Smbr			return (FALSE);
452109359Smbr	}
453109359Smbr	return (TRUE);
454109359Smbr}
455109359Smbr
45674462Salfred/*
45774462Salfred * reads data from the tcp or uip connection.
45874462Salfred * any error is fatal and the connection is closed.
45974462Salfred * (And a read of zero bytes is a half closed stream => error.)
46074462Salfred * All read operations timeout after 35 seconds.  A timeout is
46174462Salfred * fatal for the connection.
46274462Salfred */
46374462Salfredstatic int
46474462Salfredread_vc(xprtp, buf, len)
46595658Sdes	void *xprtp;
46695658Sdes	void *buf;
46774462Salfred	int len;
46874462Salfred{
46974462Salfred	SVCXPRT *xprt;
47074462Salfred	int sock;
47174462Salfred	int milliseconds = 35 * 1000;
47274462Salfred	struct pollfd pollfd;
473109359Smbr	struct cf_conn *cfp;
47474462Salfred
47595658Sdes	xprt = (SVCXPRT *)xprtp;
47674462Salfred	assert(xprt != NULL);
47774462Salfred
47874462Salfred	sock = xprt->xp_fd;
47974462Salfred
480109359Smbr	cfp = (struct cf_conn *)xprt->xp_p1;
481109359Smbr
482109359Smbr	if (cfp->nonblock) {
483116391Smbr		len = _read(sock, buf, (size_t)len);
484109359Smbr		if (len < 0) {
485109359Smbr			if (errno == EAGAIN)
486109359Smbr				len = 0;
487109359Smbr			else
488109359Smbr				goto fatal_err;
489109359Smbr		}
490109359Smbr		if (len != 0)
491109359Smbr			gettimeofday(&cfp->last_recv_time, NULL);
492109359Smbr		return len;
493109359Smbr	}
494109359Smbr
49574536Salfred	do {
49674536Salfred		pollfd.fd = sock;
49774536Salfred		pollfd.events = POLLIN;
49874462Salfred		pollfd.revents = 0;
49974536Salfred		switch (_poll(&pollfd, 1, milliseconds)) {
50074536Salfred		case -1:
50174536Salfred			if (errno == EINTR)
50274536Salfred				continue;
50374536Salfred			/*FALLTHROUGH*/
50474627Salfred		case 0:
50574627Salfred			goto fatal_err;
50674627Salfred
50774627Salfred		default:
50874627Salfred			break;
50974536Salfred		}
51074536Salfred	} while ((pollfd.revents & POLLIN) == 0);
51174462Salfred
512116391Smbr	if ((len = _read(sock, buf, (size_t)len)) > 0) {
513116391Smbr		gettimeofday(&cfp->last_recv_time, NULL);
514116391Smbr		return (len);
51574462Salfred	}
51674462Salfred
51774462Salfredfatal_err:
51874462Salfred	((struct cf_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
51974462Salfred	return (-1);
52074462Salfred}
52174462Salfred
52274462Salfred/*
52374462Salfred * writes data to the tcp connection.
52474462Salfred * Any error is fatal and the connection is closed.
52574462Salfred */
52674462Salfredstatic int
52774462Salfredwrite_vc(xprtp, buf, len)
52895658Sdes	void *xprtp;
52995658Sdes	void *buf;
53074462Salfred	int len;
53174462Salfred{
53274462Salfred	SVCXPRT *xprt;
53374462Salfred	int i, cnt;
534109359Smbr	struct cf_conn *cd;
535109359Smbr	struct timeval tv0, tv1;
53674462Salfred
53795658Sdes	xprt = (SVCXPRT *)xprtp;
53874462Salfred	assert(xprt != NULL);
539109359Smbr
540109359Smbr	cd = (struct cf_conn *)xprt->xp_p1;
541109359Smbr
542109359Smbr	if (cd->nonblock)
543109359Smbr		gettimeofday(&tv0, NULL);
54474462Salfred
545109359Smbr	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
546116391Smbr		i = _write(xprt->xp_fd, buf, (size_t)cnt);
547109359Smbr		if (i  < 0) {
548109359Smbr			if (errno != EAGAIN || !cd->nonblock) {
549109359Smbr				cd->strm_stat = XPRT_DIED;
55074462Salfred				return (-1);
55174462Salfred			}
552109359Smbr			if (cd->nonblock && i != cnt) {
553109359Smbr				/*
554109359Smbr				 * For non-blocking connections, do not
555109359Smbr				 * take more than 2 seconds writing the
556109359Smbr				 * data out.
557109359Smbr				 *
558109359Smbr				 * XXX 2 is an arbitrary amount.
559109359Smbr				 */
560109359Smbr				gettimeofday(&tv1, NULL);
561109359Smbr				if (tv1.tv_sec - tv0.tv_sec >= 2) {
562109359Smbr					cd->strm_stat = XPRT_DIED;
563109359Smbr					return (-1);
564109359Smbr				}
56574462Salfred			}
56674462Salfred		}
56774462Salfred	}
56874462Salfred
56974462Salfred	return (len);
57074462Salfred}
57174462Salfred
57274462Salfredstatic enum xprt_stat
57374462Salfredsvc_vc_stat(xprt)
57474462Salfred	SVCXPRT *xprt;
57574462Salfred{
57674462Salfred	struct cf_conn *cd;
57774462Salfred
57874462Salfred	assert(xprt != NULL);
57974462Salfred
58074462Salfred	cd = (struct cf_conn *)(xprt->xp_p1);
58174462Salfred
58274462Salfred	if (cd->strm_stat == XPRT_DIED)
58374462Salfred		return (XPRT_DIED);
58474462Salfred	if (! xdrrec_eof(&(cd->xdrs)))
58574462Salfred		return (XPRT_MOREREQS);
58674462Salfred	return (XPRT_IDLE);
58774462Salfred}
58874462Salfred
58974462Salfredstatic bool_t
59074462Salfredsvc_vc_recv(xprt, msg)
59174462Salfred	SVCXPRT *xprt;
59274462Salfred	struct rpc_msg *msg;
59374462Salfred{
59474462Salfred	struct cf_conn *cd;
59574462Salfred	XDR *xdrs;
59674462Salfred
59774462Salfred	assert(xprt != NULL);
59874462Salfred	assert(msg != NULL);
59974462Salfred
60074462Salfred	cd = (struct cf_conn *)(xprt->xp_p1);
60174462Salfred	xdrs = &(cd->xdrs);
60274462Salfred
603109359Smbr	if (cd->nonblock) {
604109359Smbr		if (!__xdrrec_getrec(xdrs, &cd->strm_stat, TRUE))
605109359Smbr			return FALSE;
606109359Smbr	}
607109359Smbr
60874462Salfred	xdrs->x_op = XDR_DECODE;
60974462Salfred	(void)xdrrec_skiprecord(xdrs);
61074462Salfred	if (xdr_callmsg(xdrs, msg)) {
61174462Salfred		cd->x_id = msg->rm_xid;
61274462Salfred		return (TRUE);
61374462Salfred	}
61474462Salfred	cd->strm_stat = XPRT_DIED;
61574462Salfred	return (FALSE);
61674462Salfred}
61774462Salfred
61874462Salfredstatic bool_t
61974462Salfredsvc_vc_getargs(xprt, xdr_args, args_ptr)
62074462Salfred	SVCXPRT *xprt;
62174462Salfred	xdrproc_t xdr_args;
62295658Sdes	void *args_ptr;
62374462Salfred{
62474462Salfred
62574462Salfred	assert(xprt != NULL);
62674462Salfred	/* args_ptr may be NULL */
62774462Salfred	return ((*xdr_args)(&(((struct cf_conn *)(xprt->xp_p1))->xdrs),
62874462Salfred	    args_ptr));
62974462Salfred}
63074462Salfred
63174462Salfredstatic bool_t
63274462Salfredsvc_vc_freeargs(xprt, xdr_args, args_ptr)
63374462Salfred	SVCXPRT *xprt;
63474462Salfred	xdrproc_t xdr_args;
63595658Sdes	void *args_ptr;
63674462Salfred{
63774462Salfred	XDR *xdrs;
63874462Salfred
63974462Salfred	assert(xprt != NULL);
64074462Salfred	/* args_ptr may be NULL */
64174462Salfred
64274462Salfred	xdrs = &(((struct cf_conn *)(xprt->xp_p1))->xdrs);
64374462Salfred
64474462Salfred	xdrs->x_op = XDR_FREE;
64574462Salfred	return ((*xdr_args)(xdrs, args_ptr));
64674462Salfred}
64774462Salfred
64874462Salfredstatic bool_t
64974462Salfredsvc_vc_reply(xprt, msg)
65074462Salfred	SVCXPRT *xprt;
65174462Salfred	struct rpc_msg *msg;
65274462Salfred{
65374462Salfred	struct cf_conn *cd;
65474462Salfred	XDR *xdrs;
655109359Smbr	bool_t rstat;
65674462Salfred
65774462Salfred	assert(xprt != NULL);
65874462Salfred	assert(msg != NULL);
65974462Salfred
66074462Salfred	cd = (struct cf_conn *)(xprt->xp_p1);
66174462Salfred	xdrs = &(cd->xdrs);
66274462Salfred
66374462Salfred	xdrs->x_op = XDR_ENCODE;
66474462Salfred	msg->rm_xid = cd->x_id;
665109359Smbr	rstat = xdr_replymsg(xdrs, msg);
66674462Salfred	(void)xdrrec_endofrecord(xdrs, TRUE);
667109359Smbr	return (rstat);
66874462Salfred}
66974462Salfred
67074462Salfredstatic void
67174462Salfredsvc_vc_ops(xprt)
67274462Salfred	SVCXPRT *xprt;
67374462Salfred{
67474462Salfred	static struct xp_ops ops;
67574462Salfred	static struct xp_ops2 ops2;
67674462Salfred	extern mutex_t ops_lock;
67774462Salfred
67874462Salfred/* VARIABLES PROTECTED BY ops_lock: ops, ops2 */
67974462Salfred
68074462Salfred	mutex_lock(&ops_lock);
68174462Salfred	if (ops.xp_recv == NULL) {
68274462Salfred		ops.xp_recv = svc_vc_recv;
68374462Salfred		ops.xp_stat = svc_vc_stat;
68474462Salfred		ops.xp_getargs = svc_vc_getargs;
68574462Salfred		ops.xp_reply = svc_vc_reply;
68674462Salfred		ops.xp_freeargs = svc_vc_freeargs;
68774462Salfred		ops.xp_destroy = svc_vc_destroy;
68874462Salfred		ops2.xp_control = svc_vc_control;
68974462Salfred	}
69074462Salfred	xprt->xp_ops = &ops;
69174462Salfred	xprt->xp_ops2 = &ops2;
69274462Salfred	mutex_unlock(&ops_lock);
69374462Salfred}
69474462Salfred
69574462Salfredstatic void
69674462Salfredsvc_vc_rendezvous_ops(xprt)
69774462Salfred	SVCXPRT *xprt;
69874462Salfred{
69974462Salfred	static struct xp_ops ops;
70074462Salfred	static struct xp_ops2 ops2;
70174462Salfred	extern mutex_t ops_lock;
70274462Salfred
70374462Salfred	mutex_lock(&ops_lock);
70474462Salfred	if (ops.xp_recv == NULL) {
70574462Salfred		ops.xp_recv = rendezvous_request;
70674462Salfred		ops.xp_stat = rendezvous_stat;
70774462Salfred		ops.xp_getargs =
70895658Sdes		    (bool_t (*)(SVCXPRT *, xdrproc_t, void *))abort;
70974462Salfred		ops.xp_reply =
71092941Sobrien		    (bool_t (*)(SVCXPRT *, struct rpc_msg *))abort;
71174462Salfred		ops.xp_freeargs =
71295658Sdes		    (bool_t (*)(SVCXPRT *, xdrproc_t, void *))abort,
71374462Salfred		ops.xp_destroy = svc_vc_destroy;
714109359Smbr		ops2.xp_control = svc_vc_rendezvous_control;
71574462Salfred	}
71674462Salfred	xprt->xp_ops = &ops;
71774462Salfred	xprt->xp_ops2 = &ops2;
71874462Salfred	mutex_unlock(&ops_lock);
71974462Salfred}
72074462Salfred
72174627Salfred/*
722116391Smbr * Get the effective UID of the sending process. Used by rpcbind, keyserv
723116391Smbr * and rpc.yppasswdd on AF_LOCAL.
72474627Salfred */
72574627Salfredint
726116391Smbr__rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) {
727116391Smbr	int sock, ret;
728116391Smbr	gid_t egid;
729116391Smbr	uid_t euid;
730116391Smbr	struct sockaddr *sa;
731116391Smbr
732116391Smbr	sock = transp->xp_fd;
733116391Smbr	sa = (struct sockaddr *)transp->xp_rtaddr.buf;
734116391Smbr	if (sa->sa_family == AF_LOCAL) {
735116391Smbr		ret = getpeereid(sock, &euid, &egid);
736116391Smbr		if (ret == 0)
737116391Smbr			*uid = euid;
738116391Smbr		return (ret);
739116391Smbr	} else
74090257Salfred		return (-1);
74174627Salfred}
742109359Smbr
743109359Smbr/*
744109359Smbr * Destroy xprts that have not have had any activity in 'timeout' seconds.
745109359Smbr * If 'cleanblock' is true, blocking connections (the default) are also
746109359Smbr * cleaned. If timeout is 0, the least active connection is picked.
747109359Smbr */
748109359Smbrbool_t
749109359Smbr__svc_clean_idle(fd_set *fds, int timeout, bool_t cleanblock)
750109359Smbr{
751109359Smbr	int i, ncleaned;
752109359Smbr	SVCXPRT *xprt, *least_active;
753109359Smbr	struct timeval tv, tdiff, tmax;
754109359Smbr	struct cf_conn *cd;
755109359Smbr
756109359Smbr	gettimeofday(&tv, NULL);
757109359Smbr	tmax.tv_sec = tmax.tv_usec = 0;
758109359Smbr	least_active = NULL;
759109359Smbr	rwlock_wrlock(&svc_fd_lock);
760109359Smbr	for (i = ncleaned = 0; i <= svc_maxfd; i++) {
761109359Smbr		if (FD_ISSET(i, fds)) {
762109359Smbr			xprt = __svc_xports[i];
763109359Smbr			if (xprt == NULL || xprt->xp_ops == NULL ||
764109359Smbr			    xprt->xp_ops->xp_recv != svc_vc_recv)
765109359Smbr				continue;
766109359Smbr			cd = (struct cf_conn *)xprt->xp_p1;
767109359Smbr			if (!cleanblock && !cd->nonblock)
768109359Smbr				continue;
769109359Smbr			if (timeout == 0) {
770109359Smbr				timersub(&tv, &cd->last_recv_time, &tdiff);
771109359Smbr				if (timercmp(&tdiff, &tmax, >)) {
772109359Smbr					tmax = tdiff;
773109359Smbr					least_active = xprt;
774109359Smbr				}
775109359Smbr				continue;
776109359Smbr			}
777109359Smbr			if (tv.tv_sec - cd->last_recv_time.tv_sec > timeout) {
778109359Smbr				__xprt_unregister_unlocked(xprt);
779109359Smbr				__svc_vc_dodestroy(xprt);
780109359Smbr				ncleaned++;
781109359Smbr			}
782109359Smbr		}
783109359Smbr	}
784109359Smbr	if (timeout == 0 && least_active != NULL) {
785109359Smbr		__xprt_unregister_unlocked(least_active);
786109359Smbr		__svc_vc_dodestroy(least_active);
787109359Smbr		ncleaned++;
788109359Smbr	}
789109359Smbr	rwlock_unlock(&svc_fd_lock);
790109359Smbr	return ncleaned > 0 ? TRUE : FALSE;
791109359Smbr}
792