svc_vc.c revision 267654
119370Spst/*	$NetBSD: svc_vc.c,v 1.7 2000/08/03 00:01:53 fvdl Exp $	*/
298948Sobrien
3130809Smarcel/*-
498948Sobrien * Copyright (c) 2009, Sun Microsystems, Inc.
519370Spst * All rights reserved.
619370Spst *
798948Sobrien * Redistribution and use in source and binary forms, with or without
819370Spst * modification, are permitted provided that the following conditions are met:
998948Sobrien * - Redistributions of source code must retain the above copyright notice,
1098948Sobrien *   this list of conditions and the following disclaimer.
1198948Sobrien * - Redistributions in binary form must reproduce the above copyright notice,
1298948Sobrien *   this list of conditions and the following disclaimer in the documentation
1319370Spst *   and/or other materials provided with the distribution.
1498948Sobrien * - Neither the name of Sun Microsystems, Inc. nor the names of its
1598948Sobrien *   contributors may be used to endorse or promote products derived
1698948Sobrien *   from this software without specific prior written permission.
1798948Sobrien *
1819370Spst * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1998948Sobrien * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2098948Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2198948Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2298948Sobrien * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2319370Spst * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2419370Spst * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2519370Spst * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2619370Spst * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2746283Sdfr * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2819370Spst * POSSIBILITY OF SUCH DAMAGE.
2919370Spst */
3019370Spst
31130809Smarcel#if defined(LIBC_SCCS) && !defined(lint)
3219370Spststatic char *sccsid2 = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
3319370Spststatic char *sccsid = "@(#)svc_tcp.c	2.2 88/08/01 4.0 RPCSRC";
3419370Spst#endif
3519370Spst#include <sys/cdefs.h>
3619370Spst__FBSDID("$FreeBSD: releng/9.3/sys/rpc/svc_vc.c 261082 2014-01-23 17:27:16Z mav $");
3719370Spst
3819370Spst/*
3919370Spst * svc_vc.c, Server side for Connection Oriented based RPC.
4019370Spst *
4119370Spst * Actually implements two flavors of transporter -
4219370Spst * a tcp rendezvouser (a listner and connection establisher)
4319370Spst * and a record/tcp stream.
4498948Sobrien */
45130809Smarcel
46130809Smarcel#include <sys/param.h>
4719370Spst#include <sys/limits.h>
48130809Smarcel#include <sys/lock.h>
49130809Smarcel#include <sys/kernel.h>
5098948Sobrien#include <sys/malloc.h>
5119370Spst#include <sys/mbuf.h>
5298948Sobrien#include <sys/mutex.h>
5398948Sobrien#include <sys/proc.h>
5498948Sobrien#include <sys/protosw.h>
5598948Sobrien#include <sys/queue.h>
5619370Spst#include <sys/socket.h>
5798948Sobrien#include <sys/socketvar.h>
5898948Sobrien#include <sys/sx.h>
5998948Sobrien#include <sys/systm.h>
6098948Sobrien#include <sys/uio.h>
6198948Sobrien
6298948Sobrien#include <net/vnet.h>
6398948Sobrien
6419370Spst#include <netinet/tcp.h>
6519370Spst
6619370Spst#include <rpc/rpc.h>
6719370Spst
6819370Spst#include <rpc/krpc.h>
6919370Spst#include <rpc/rpc_com.h>
7019370Spst
7119370Spst#include <security/mac/mac_framework.h>
7219370Spst
7319370Spststatic bool_t svc_vc_rendezvous_recv(SVCXPRT *, struct rpc_msg *,
7419370Spst    struct sockaddr **, struct mbuf **);
7519370Spststatic enum xprt_stat svc_vc_rendezvous_stat(SVCXPRT *);
7619370Spststatic void svc_vc_rendezvous_destroy(SVCXPRT *);
7719370Spststatic bool_t svc_vc_null(void);
7819370Spststatic void svc_vc_destroy(SVCXPRT *);
7919370Spststatic enum xprt_stat svc_vc_stat(SVCXPRT *);
8019370Spststatic bool_t svc_vc_ack(SVCXPRT *, uint32_t *);
8119370Spststatic bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *,
8219370Spst    struct sockaddr **, struct mbuf **);
8319370Spststatic bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *,
8419370Spst    struct sockaddr *, struct mbuf *, uint32_t *seq);
8519370Spststatic bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in);
8619370Spststatic bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq,
8719370Spst    void *in);
8819370Spststatic void svc_vc_backchannel_destroy(SVCXPRT *);
8919370Spststatic enum xprt_stat svc_vc_backchannel_stat(SVCXPRT *);
9019370Spststatic bool_t svc_vc_backchannel_recv(SVCXPRT *, struct rpc_msg *,
9119370Spst    struct sockaddr **, struct mbuf **);
9219370Spststatic bool_t svc_vc_backchannel_reply(SVCXPRT *, struct rpc_msg *,
9319370Spst    struct sockaddr *, struct mbuf *, uint32_t *);
9419370Spststatic bool_t svc_vc_backchannel_control(SVCXPRT *xprt, const u_int rq,
9519370Spst    void *in);
9619370Spststatic SVCXPRT *svc_vc_create_conn(SVCPOOL *pool, struct socket *so,
9719370Spst    struct sockaddr *raddr);
9819370Spststatic int svc_vc_accept(struct socket *head, struct socket **sop);
9998948Sobrienstatic int svc_vc_soupcall(struct socket *so, void *arg, int waitflag);
10098948Sobrien
10198948Sobrienstatic struct xp_ops svc_vc_rendezvous_ops = {
10298948Sobrien	.xp_recv =	svc_vc_rendezvous_recv,
10319370Spst	.xp_stat =	svc_vc_rendezvous_stat,
10419370Spst	.xp_reply =	(bool_t (*)(SVCXPRT *, struct rpc_msg *,
10519370Spst		struct sockaddr *, struct mbuf *, uint32_t *))svc_vc_null,
10619370Spst	.xp_destroy =	svc_vc_rendezvous_destroy,
10719370Spst	.xp_control =	svc_vc_rendezvous_control
10898948Sobrien};
10919370Spst
11019370Spststatic struct xp_ops svc_vc_ops = {
11119370Spst	.xp_recv =	svc_vc_recv,
11219370Spst	.xp_stat =	svc_vc_stat,
11319370Spst	.xp_ack =	svc_vc_ack,
11498948Sobrien	.xp_reply =	svc_vc_reply,
11598948Sobrien	.xp_destroy =	svc_vc_destroy,
11698948Sobrien	.xp_control =	svc_vc_control
11719370Spst};
11846283Sdfr
11946283Sdfrstatic struct xp_ops svc_vc_backchannel_ops = {
12046283Sdfr	.xp_recv =	svc_vc_backchannel_recv,
12146283Sdfr	.xp_stat =	svc_vc_backchannel_stat,
12219370Spst	.xp_reply =	svc_vc_backchannel_reply,
12319370Spst	.xp_destroy =	svc_vc_backchannel_destroy,
12419370Spst	.xp_control =	svc_vc_backchannel_control
12519370Spst};
12619370Spst
12719370Spst/*
12819370Spst * Usage:
12998948Sobrien *	xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
13098948Sobrien *
13198948Sobrien * Creates, registers, and returns a (rpc) tcp based transporter.
13298948Sobrien * Once *xprt is initialized, it is registered as a transporter
13398948Sobrien * see (svc.h, xprt_register).  This routine returns
13498948Sobrien * a NULL if a problem occurred.
13598948Sobrien *
13698948Sobrien * The filedescriptor passed in is expected to refer to a bound, but
13798948Sobrien * not yet connected socket.
13898948Sobrien *
13919370Spst * Since streams do buffered io similar to stdio, the caller can specify
14098948Sobrien * how big the send and receive buffers are via the second and third parms;
14119370Spst * 0 => use the system default.
14298948Sobrien */
14319370SpstSVCXPRT *
14498948Sobriensvc_vc_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
14598948Sobrien    size_t recvsize)
14619370Spst{
14798948Sobrien	SVCXPRT *xprt;
14898948Sobrien	struct sockaddr* sa;
14919370Spst	int error;
15098948Sobrien
15198948Sobrien	SOCK_LOCK(so);
15298948Sobrien	if (so->so_state & (SS_ISCONNECTED|SS_ISDISCONNECTED)) {
15319370Spst		SOCK_UNLOCK(so);
15498948Sobrien		error = so->so_proto->pr_usrreqs->pru_peeraddr(so, &sa);
15519370Spst		if (error)
15698948Sobrien			return (NULL);
15798948Sobrien		xprt = svc_vc_create_conn(pool, so, sa);
15898948Sobrien		free(sa, M_SONAME);
15919370Spst		return (xprt);
16098948Sobrien	}
16119370Spst	SOCK_UNLOCK(so);
16298948Sobrien
16319370Spst	xprt = svc_xprt_alloc();
16498948Sobrien	sx_init(&xprt->xp_lock, "xprt->xp_lock");
16519370Spst	xprt->xp_pool = pool;
16698948Sobrien	xprt->xp_socket = so;
16719370Spst	xprt->xp_p1 = NULL;
16898948Sobrien	xprt->xp_p2 = NULL;
16919370Spst	xprt->xp_ops = &svc_vc_rendezvous_ops;
17098948Sobrien
17119370Spst	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
17298948Sobrien	if (error) {
17319370Spst		goto cleanup_svc_vc_create;
17498948Sobrien	}
17519370Spst
17698948Sobrien	memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
17719370Spst	free(sa, M_SONAME);
17898948Sobrien
17919370Spst	xprt_register(xprt);
18098948Sobrien
18198948Sobrien	solisten(so, SOMAXCONN, curthread);
18219370Spst
18398948Sobrien	SOCKBUF_LOCK(&so->so_rcv);
18419370Spst	xprt->xp_upcallset = 1;
18519370Spst	soupcall_set(so, SO_RCV, svc_vc_soupcall, xprt);
18619370Spst	SOCKBUF_UNLOCK(&so->so_rcv);
18719370Spst
18819370Spst	return (xprt);
18919370Spstcleanup_svc_vc_create:
19019370Spst	if (xprt) {
19119370Spst		sx_destroy(&xprt->xp_lock);
19219370Spst		svc_xprt_free(xprt);
19319370Spst	}
19419370Spst	return (NULL);
19519370Spst}
19698948Sobrien
19719370Spst/*
198130809Smarcel * Create a new transport for a socket optained via soaccept().
19919370Spst */
20019370SpstSVCXPRT *
20119370Spstsvc_vc_create_conn(SVCPOOL *pool, struct socket *so, struct sockaddr *raddr)
20219370Spst{
203130809Smarcel	SVCXPRT *xprt = NULL;
20419370Spst	struct cf_conn *cd = NULL;
20519370Spst	struct sockaddr* sa = NULL;
20619370Spst	struct sockopt opt;
20719370Spst	int one = 1;
20819370Spst	int error;
20919370Spst
21019370Spst	bzero(&opt, sizeof(struct sockopt));
21119370Spst	opt.sopt_dir = SOPT_SET;
212130809Smarcel	opt.sopt_level = SOL_SOCKET;
21319370Spst	opt.sopt_name = SO_KEEPALIVE;
21419370Spst	opt.sopt_val = &one;
21519370Spst	opt.sopt_valsize = sizeof(one);
21619370Spst	error = sosetopt(so, &opt);
21719370Spst	if (error) {
21819370Spst		return (NULL);
21919370Spst	}
22019370Spst
22119370Spst	if (so->so_proto->pr_protocol == IPPROTO_TCP) {
22219370Spst		bzero(&opt, sizeof(struct sockopt));
22398948Sobrien		opt.sopt_dir = SOPT_SET;
22419370Spst		opt.sopt_level = IPPROTO_TCP;
22519370Spst		opt.sopt_name = TCP_NODELAY;
22619370Spst		opt.sopt_val = &one;
22719370Spst		opt.sopt_valsize = sizeof(one);
22819370Spst		error = sosetopt(so, &opt);
22919370Spst		if (error) {
23019370Spst			return (NULL);
23119370Spst		}
23219370Spst	}
23319370Spst
23419370Spst	cd = mem_alloc(sizeof(*cd));
23519370Spst	cd->strm_stat = XPRT_IDLE;
23619370Spst
23719370Spst	xprt = svc_xprt_alloc();
23898948Sobrien	sx_init(&xprt->xp_lock, "xprt->xp_lock");
23998948Sobrien	xprt->xp_pool = pool;
24098948Sobrien	xprt->xp_socket = so;
24119370Spst	xprt->xp_p1 = cd;
24219370Spst	xprt->xp_p2 = NULL;
24319370Spst	xprt->xp_ops = &svc_vc_ops;
24419370Spst
24519370Spst	/*
24698948Sobrien	 * See http://www.connectathon.org/talks96/nfstcp.pdf - client
24719370Spst	 * has a 5 minute timer, server has a 6 minute timer.
24898948Sobrien	 */
24998948Sobrien	xprt->xp_idletimeout = 6 * 60;
25098948Sobrien
25198948Sobrien	memcpy(&xprt->xp_rtaddr, raddr, raddr->sa_len);
25298948Sobrien
25319370Spst	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
25498948Sobrien	if (error)
25598948Sobrien		goto cleanup_svc_vc_create;
25619370Spst
25798948Sobrien	memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
25819370Spst	free(sa, M_SONAME);
25946283Sdfr
26019370Spst	xprt_register(xprt);
26119370Spst
26219370Spst	SOCKBUF_LOCK(&so->so_rcv);
26319370Spst	xprt->xp_upcallset = 1;
26498948Sobrien	soupcall_set(so, SO_RCV, svc_vc_soupcall, xprt);
26519370Spst	SOCKBUF_UNLOCK(&so->so_rcv);
26646283Sdfr
26746283Sdfr	/*
26898948Sobrien	 * Throw the transport into the active list in case it already
26946283Sdfr	 * has some data buffered.
27019370Spst	 */
27146283Sdfr	sx_xlock(&xprt->xp_lock);
27219370Spst	xprt_active(xprt);
27346283Sdfr	sx_xunlock(&xprt->xp_lock);
27446283Sdfr
27546283Sdfr	return (xprt);
27646283Sdfrcleanup_svc_vc_create:
27798948Sobrien	if (xprt) {
27846283Sdfr		sx_destroy(&xprt->xp_lock);
27998948Sobrien		svc_xprt_free(xprt);
28046283Sdfr	}
28198948Sobrien	if (cd)
28298948Sobrien		mem_free(cd, sizeof(*cd));
28346283Sdfr	return (NULL);
28419370Spst}
28519370Spst
28619370Spst/*
28746283Sdfr * Create a new transport for a backchannel on a clnt_vc socket.
28846283Sdfr */
28998948SobrienSVCXPRT *
29046283Sdfrsvc_vc_create_backchannel(SVCPOOL *pool)
29146283Sdfr{
29298948Sobrien	SVCXPRT *xprt = NULL;
29346283Sdfr	struct cf_conn *cd = NULL;
29446283Sdfr
29546283Sdfr	cd = mem_alloc(sizeof(*cd));
29646283Sdfr	cd->strm_stat = XPRT_IDLE;
29746283Sdfr
29846283Sdfr	xprt = svc_xprt_alloc();
29946283Sdfr	sx_init(&xprt->xp_lock, "xprt->xp_lock");
30046283Sdfr	xprt->xp_pool = pool;
30146283Sdfr	xprt->xp_socket = NULL;
30246283Sdfr	xprt->xp_p1 = cd;
30346283Sdfr	xprt->xp_p2 = NULL;
30446283Sdfr	xprt->xp_ops = &svc_vc_backchannel_ops;
30546283Sdfr	return (xprt);
30619370Spst}
30719370Spst
30819370Spst/*
30919370Spst * This does all of the accept except the final call to soaccept. The
31019370Spst * caller will call soaccept after dropping its locks (soaccept may
31119370Spst * call malloc).
31219370Spst */
31319370Spstint
314130809Smarcelsvc_vc_accept(struct socket *head, struct socket **sop)
31519370Spst{
31619370Spst	int error = 0;
31719370Spst	struct socket *so;
31819370Spst
31919370Spst	if ((head->so_options & SO_ACCEPTCONN) == 0) {
32019370Spst		error = EINVAL;
32198948Sobrien		goto done;
32219370Spst	}
32319370Spst#ifdef MAC
32419370Spst	error = mac_socket_check_accept(curthread->td_ucred, head);
32519370Spst	if (error != 0)
32619370Spst		goto done;
32719370Spst#endif
32898948Sobrien	ACCEPT_LOCK();
32919370Spst	if (TAILQ_EMPTY(&head->so_comp)) {
33019370Spst		ACCEPT_UNLOCK();
33119370Spst		error = EWOULDBLOCK;
33219370Spst		goto done;
33319370Spst	}
33419370Spst	so = TAILQ_FIRST(&head->so_comp);
33519370Spst	KASSERT(!(so->so_qstate & SQ_INCOMP), ("svc_vc_accept: so SQ_INCOMP"));
33619370Spst	KASSERT(so->so_qstate & SQ_COMP, ("svc_vc_accept: so not SQ_COMP"));
33719370Spst
33898948Sobrien	/*
33919370Spst	 * Before changing the flags on the socket, we have to bump the
340130809Smarcel	 * reference count.  Otherwise, if the protocol calls sofree(),
341130809Smarcel	 * the socket will be released due to a zero refcount.
34219370Spst	 * XXX might not need soref() since this is simpler than kern_accept.
34319370Spst	 */
34419370Spst	SOCK_LOCK(so);			/* soref() and so_state update */
34519370Spst	soref(so);			/* file descriptor reference */
34619370Spst
34719370Spst	TAILQ_REMOVE(&head->so_comp, so, so_list);
34819370Spst	head->so_qlen--;
34919370Spst	so->so_state |= (head->so_state & SS_NBIO);
35019370Spst	so->so_qstate &= ~SQ_COMP;
35119370Spst	so->so_head = NULL;
35219370Spst
35319370Spst	SOCK_UNLOCK(so);
35419370Spst	ACCEPT_UNLOCK();
35519370Spst
35619370Spst	*sop = so;
35719370Spst
35819370Spst	/* connection has been removed from the listen queue */
35998948Sobrien	KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0);
36019370Spstdone:
36119370Spst	return (error);
36298948Sobrien}
36398948Sobrien
36498948Sobrien/*ARGSUSED*/
36598948Sobrienstatic bool_t
36698948Sobriensvc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg,
36798948Sobrien    struct sockaddr **addrp, struct mbuf **mp)
36898948Sobrien{
36998948Sobrien	struct socket *so = NULL;
37098948Sobrien	struct sockaddr *sa = NULL;
37198948Sobrien	int error;
37246283Sdfr	SVCXPRT *new_xprt;
37319370Spst
37419370Spst	/*
37519370Spst	 * The socket upcall calls xprt_active() which will eventually
37619370Spst	 * cause the server to call us here. We attempt to accept a
37719370Spst	 * connection from the socket and turn it into a new
37819370Spst	 * transport. If the accept fails, we have drained all pending
37919370Spst	 * connections so we call xprt_inactive().
38019370Spst	 */
38198948Sobrien	sx_xlock(&xprt->xp_lock);
38219370Spst
38319370Spst	error = svc_vc_accept(xprt->xp_socket, &so);
38498948Sobrien
38519370Spst	if (error == EWOULDBLOCK) {
38619370Spst		/*
38719370Spst		 * We must re-test for new connections after taking
38819370Spst		 * the lock to protect us in the case where a new
38998948Sobrien		 * connection arrives after our call to accept fails
39098948Sobrien		 * with EWOULDBLOCK.
39119370Spst		 */
392130809Smarcel		ACCEPT_LOCK();
393130809Smarcel		if (TAILQ_EMPTY(&xprt->xp_socket->so_comp))
39419370Spst			xprt_inactive_self(xprt);
39519370Spst		ACCEPT_UNLOCK();
39619370Spst		sx_xunlock(&xprt->xp_lock);
39719370Spst		return (FALSE);
39819370Spst	}
39919370Spst
40019370Spst	if (error) {
40119370Spst		SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
40219370Spst		if (xprt->xp_upcallset) {
40398948Sobrien			xprt->xp_upcallset = 0;
40419370Spst			soupcall_clear(xprt->xp_socket, SO_RCV);
40519370Spst		}
40619370Spst		SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
40719370Spst		xprt_inactive_self(xprt);
40819370Spst		sx_xunlock(&xprt->xp_lock);
40998948Sobrien		return (FALSE);
41019370Spst	}
41119370Spst
41219370Spst	sx_xunlock(&xprt->xp_lock);
41319370Spst
41419370Spst	sa = 0;
41519370Spst	error = soaccept(so, &sa);
41619370Spst
41719370Spst	if (error) {
41819370Spst		/*
41998948Sobrien		 * XXX not sure if I need to call sofree or soclose here.
42098948Sobrien		 */
42119370Spst		if (sa)
42219370Spst			free(sa, M_SONAME);
42398948Sobrien		return (FALSE);
42498948Sobrien	}
42519370Spst
42646283Sdfr	/*
42719370Spst	 * svc_vc_create_conn will call xprt_register - we don't need
42819370Spst	 * to do anything with the new connection except derefence it.
42919370Spst	 */
43019370Spst	new_xprt = svc_vc_create_conn(xprt->xp_pool, so, sa);
43119370Spst	if (!new_xprt) {
43219370Spst		soclose(so);
43319370Spst	} else {
43419370Spst		SVC_RELEASE(new_xprt);
43519370Spst	}
43619370Spst
43719370Spst	free(sa, M_SONAME);
43819370Spst
43919370Spst	return (FALSE); /* there is never an rpc msg to be processed */
44019370Spst}
44119370Spst
44219370Spst/*ARGSUSED*/
44398948Sobrienstatic enum xprt_stat
44419370Spstsvc_vc_rendezvous_stat(SVCXPRT *xprt)
44519370Spst{
44646283Sdfr
44719370Spst	return (XPRT_IDLE);
44819370Spst}
44998948Sobrien
45098948Sobrienstatic void
45119370Spstsvc_vc_destroy_common(SVCXPRT *xprt)
45219370Spst{
45319370Spst	SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
45419370Spst	if (xprt->xp_upcallset) {
45519370Spst		xprt->xp_upcallset = 0;
45619370Spst		soupcall_clear(xprt->xp_socket, SO_RCV);
45719370Spst	}
45819370Spst	SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
45919370Spst
46019370Spst	if (xprt->xp_socket)
46119370Spst		(void)soclose(xprt->xp_socket);
46219370Spst
46319370Spst	if (xprt->xp_netid)
46419370Spst		(void) mem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1);
46519370Spst	svc_xprt_free(xprt);
46619370Spst}
46719370Spst
46819370Spststatic void
46919370Spstsvc_vc_rendezvous_destroy(SVCXPRT *xprt)
47019370Spst{
471130809Smarcel
47219370Spst	svc_vc_destroy_common(xprt);
47319370Spst}
47419370Spst
47519370Spststatic void
47619370Spstsvc_vc_destroy(SVCXPRT *xprt)
47719370Spst{
47819370Spst	struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1;
47919370Spst
48019370Spst	svc_vc_destroy_common(xprt);
48119370Spst
48219370Spst	if (cd->mreq)
48319370Spst		m_freem(cd->mreq);
48419370Spst	if (cd->mpending)
48598948Sobrien		m_freem(cd->mpending);
48619370Spst	mem_free(cd, sizeof(*cd));
48719370Spst}
48819370Spst
48919370Spststatic void
49019370Spstsvc_vc_backchannel_destroy(SVCXPRT *xprt)
49119370Spst{
49219370Spst	struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1;
49319370Spst	struct mbuf *m, *m2;
49419370Spst
49519370Spst	svc_xprt_free(xprt);
49619370Spst	m = cd->mreq;
49719370Spst	while (m != NULL) {
49819370Spst		m2 = m;
49919370Spst		m = m->m_nextpkt;
50019370Spst		m_freem(m2);
50119370Spst	}
50219370Spst	mem_free(cd, sizeof(*cd));
50319370Spst}
50419370Spst
50519370Spst/*ARGSUSED*/
50619370Spststatic bool_t
50798948Sobriensvc_vc_control(SVCXPRT *xprt, const u_int rq, void *in)
50819370Spst{
50919370Spst	return (FALSE);
51019370Spst}
51119370Spst
51219370Spststatic bool_t
51319370Spstsvc_vc_rendezvous_control(SVCXPRT *xprt, const u_int rq, void *in)
514130809Smarcel{
51598948Sobrien
51619370Spst	return (FALSE);
51719370Spst}
518130809Smarcel
51919370Spststatic bool_t
52098948Sobriensvc_vc_backchannel_control(SVCXPRT *xprt, const u_int rq, void *in)
52198948Sobrien{
52298948Sobrien
52398948Sobrien	return (FALSE);
52446283Sdfr}
52598948Sobrien
52619370Spststatic enum xprt_stat
52719370Spstsvc_vc_stat(SVCXPRT *xprt)
52898948Sobrien{
52998948Sobrien	struct cf_conn *cd;
53098948Sobrien
53198948Sobrien	cd = (struct cf_conn *)(xprt->xp_p1);
53219370Spst
53319370Spst	if (cd->strm_stat == XPRT_DIED)
53419370Spst		return (XPRT_DIED);
53519370Spst
53619370Spst	if (cd->mreq != NULL && cd->resid == 0 && cd->eor)
53798948Sobrien		return (XPRT_MOREREQS);
53819370Spst
53998948Sobrien	if (soreadable(xprt->xp_socket))
54098948Sobrien		return (XPRT_MOREREQS);
54198948Sobrien
54219370Spst	return (XPRT_IDLE);
54319370Spst}
54419370Spst
54519370Spststatic bool_t
54698948Sobriensvc_vc_ack(SVCXPRT *xprt, uint32_t *ack)
54719370Spst{
54898948Sobrien
54946283Sdfr	*ack = atomic_load_acq_32(&xprt->xp_snt_cnt);
55046283Sdfr	*ack -= xprt->xp_socket->so_snd.sb_cc;
55146283Sdfr	return (TRUE);
55246283Sdfr}
55346283Sdfr
55446283Sdfrstatic enum xprt_stat
55598948Sobriensvc_vc_backchannel_stat(SVCXPRT *xprt)
55698948Sobrien{
55798948Sobrien	struct cf_conn *cd;
55846283Sdfr
55919370Spst	cd = (struct cf_conn *)(xprt->xp_p1);
56019370Spst
56119370Spst	if (cd->mreq != NULL)
56219370Spst		return (XPRT_MOREREQS);
56319370Spst
564130809Smarcel	return (XPRT_IDLE);
56519370Spst}
566130809Smarcel
567130809Smarcel/*
568130809Smarcel * If we have an mbuf chain in cd->mpending, try to parse a record from it,
569130809Smarcel * leaving the result in cd->mreq. If we don't have a complete record, leave
570130809Smarcel * the partial result in cd->mreq and try to read more from the socket.
571130809Smarcel */
572130809Smarcelstatic int
573130809Smarcelsvc_vc_process_pending(SVCXPRT *xprt)
574130809Smarcel{
575130809Smarcel	struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
576130809Smarcel	struct socket *so = xprt->xp_socket;
577130809Smarcel	struct mbuf *m;
578130809Smarcel
579130809Smarcel	/*
580130809Smarcel	 * If cd->resid is non-zero, we have part of the
581130809Smarcel	 * record already, otherwise we are expecting a record
582130809Smarcel	 * marker.
583130809Smarcel	 */
584130809Smarcel	if (!cd->resid && cd->mpending) {
585130809Smarcel		/*
586130809Smarcel		 * See if there is enough data buffered to
587130809Smarcel		 * make up a record marker. Make sure we can
588130809Smarcel		 * handle the case where the record marker is
589130809Smarcel		 * split across more than one mbuf.
59019370Spst		 */
59119370Spst		size_t n = 0;
59298948Sobrien		uint32_t header;
59319370Spst
59419370Spst		m = cd->mpending;
59519370Spst		while (n < sizeof(uint32_t) && m) {
59619370Spst			n += m->m_len;
59719370Spst			m = m->m_next;
598130809Smarcel		}
59919370Spst		if (n < sizeof(uint32_t)) {
60019370Spst			so->so_rcv.sb_lowat = sizeof(uint32_t) - n;
60119370Spst			return (FALSE);
60219370Spst		}
60398948Sobrien		m_copydata(cd->mpending, 0, sizeof(header),
60419370Spst		    (char *)&header);
60519370Spst		header = ntohl(header);
60619370Spst		cd->eor = (header & 0x80000000) != 0;
60719370Spst		cd->resid = header & 0x7fffffff;
60819370Spst		m_adj(cd->mpending, sizeof(uint32_t));
60919370Spst	}
610130809Smarcel
611130809Smarcel	/*
612130809Smarcel	 * Start pulling off mbufs from cd->mpending
61398948Sobrien	 * until we either have a complete record or
61419370Spst	 * we run out of data. We use m_split to pull
61519370Spst	 * data - it will pull as much as possible and
61619370Spst	 * split the last mbuf if necessary.
61798948Sobrien	 */
61898948Sobrien	while (cd->mpending && cd->resid) {
61998948Sobrien		m = cd->mpending;
62098948Sobrien		if (cd->mpending->m_next
62198948Sobrien		    || cd->mpending->m_len > cd->resid)
62298948Sobrien			cd->mpending = m_split(cd->mpending,
62398948Sobrien			    cd->resid, M_WAITOK);
62498948Sobrien		else
62519370Spst			cd->mpending = NULL;
62698948Sobrien		if (cd->mreq)
62719370Spst			m_last(cd->mreq)->m_next = m;
62819370Spst		else
62919370Spst			cd->mreq = m;
63019370Spst		while (m) {
63119370Spst			cd->resid -= m->m_len;
63219370Spst			m = m->m_next;
63319370Spst		}
63419370Spst	}
63519370Spst
63619370Spst	/*
63798948Sobrien	 * Block receive upcalls if we have more data pending,
63898948Sobrien	 * otherwise report our need.
63998948Sobrien	 */
64098948Sobrien	if (cd->mpending)
64198948Sobrien		so->so_rcv.sb_lowat = INT_MAX;
64219370Spst	else
64319370Spst		so->so_rcv.sb_lowat =
64419370Spst		    imax(1, imin(cd->resid, so->so_rcv.sb_hiwat / 2));
64519370Spst	return (TRUE);
64619370Spst}
64798948Sobrien
64819370Spststatic bool_t
64919370Spstsvc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
65019370Spst    struct sockaddr **addrp, struct mbuf **mp)
65119370Spst{
65219370Spst	struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
65319370Spst	struct uio uio;
65419370Spst	struct mbuf *m;
65519370Spst	struct socket* so = xprt->xp_socket;
65619370Spst	XDR xdrs;
65798948Sobrien	int error, rcvflag;
65819370Spst
65998948Sobrien	/*
66019370Spst	 * Serialise access to the socket and our own record parsing
66198948Sobrien	 * state.
66219370Spst	 */
66398948Sobrien	sx_xlock(&xprt->xp_lock);
66498948Sobrien
66598948Sobrien	for (;;) {
66619370Spst		/* If we have no request ready, check pending queue. */
66798948Sobrien		while (cd->mpending &&
66819370Spst		    (cd->mreq == NULL || cd->resid != 0 || !cd->eor)) {
66919370Spst			if (!svc_vc_process_pending(xprt))
67019370Spst				break;
67119370Spst		}
67219370Spst
67319370Spst		/* Process and return complete request in cd->mreq. */
67419370Spst		if (cd->mreq != NULL && cd->resid == 0 && cd->eor) {
67598948Sobrien
67698948Sobrien			xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE);
67719370Spst			cd->mreq = NULL;
678130809Smarcel
67919370Spst			/* Check for next request in a pending queue. */
680130809Smarcel			svc_vc_process_pending(xprt);
68119370Spst			if (cd->mreq == NULL || cd->resid != 0) {
68219370Spst				SOCKBUF_LOCK(&so->so_rcv);
68319370Spst				if (!soreadable(so))
68419370Spst					xprt_inactive_self(xprt);
68519370Spst				SOCKBUF_UNLOCK(&so->so_rcv);
68619370Spst			}
68719370Spst
68819370Spst			sx_xunlock(&xprt->xp_lock);
68919370Spst
69019370Spst			if (! xdr_callmsg(&xdrs, msg)) {
69119370Spst				XDR_DESTROY(&xdrs);
69219370Spst				return (FALSE);
69319370Spst			}
69498948Sobrien
69519370Spst			*addrp = NULL;
69619370Spst			*mp = xdrmbuf_getall(&xdrs);
69719370Spst			XDR_DESTROY(&xdrs);
69819370Spst
69919370Spst			return (TRUE);
70019370Spst		}
70119370Spst
70219370Spst		/*
70319370Spst		 * The socket upcall calls xprt_active() which will eventually
70419370Spst		 * cause the server to call us here. We attempt to
70519370Spst		 * read as much as possible from the socket and put
70619370Spst		 * the result in cd->mpending. If the read fails,
70719370Spst		 * we have drained both cd->mpending and the socket so
70819370Spst		 * we can call xprt_inactive().
70919370Spst		 */
71019370Spst		uio.uio_resid = 1000000000;
71119370Spst		uio.uio_td = curthread;
71219370Spst		m = NULL;
71319370Spst		rcvflag = MSG_DONTWAIT;
71419370Spst		error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag);
71519370Spst
71619370Spst		if (error == EWOULDBLOCK) {
71719370Spst			/*
71819370Spst			 * We must re-test for readability after
71919370Spst			 * taking the lock to protect us in the case
72019370Spst			 * where a new packet arrives on the socket
72119370Spst			 * after our call to soreceive fails with
72219370Spst			 * EWOULDBLOCK.
72319370Spst			 */
72419370Spst			SOCKBUF_LOCK(&so->so_rcv);
72519370Spst			if (!soreadable(so))
72619370Spst				xprt_inactive_self(xprt);
72719370Spst			SOCKBUF_UNLOCK(&so->so_rcv);
72898948Sobrien			sx_xunlock(&xprt->xp_lock);
72998948Sobrien			return (FALSE);
73019370Spst		}
73119370Spst
73219370Spst		if (error) {
73319370Spst			SOCKBUF_LOCK(&so->so_rcv);
73419370Spst			if (xprt->xp_upcallset) {
73546283Sdfr				xprt->xp_upcallset = 0;
73619370Spst				soupcall_clear(so, SO_RCV);
73719370Spst			}
73819370Spst			SOCKBUF_UNLOCK(&so->so_rcv);
73919370Spst			xprt_inactive_self(xprt);
74019370Spst			cd->strm_stat = XPRT_DIED;
74119370Spst			sx_xunlock(&xprt->xp_lock);
74219370Spst			return (FALSE);
74319370Spst		}
74419370Spst
74519370Spst		if (!m) {
74619370Spst			/*
74719370Spst			 * EOF - the other end has closed the socket.
74819370Spst			 */
74946283Sdfr			xprt_inactive_self(xprt);
75019370Spst			cd->strm_stat = XPRT_DIED;
75119370Spst			sx_xunlock(&xprt->xp_lock);
75219370Spst			return (FALSE);
75319370Spst		}
75419370Spst
75519370Spst		if (cd->mpending)
75619370Spst			m_last(cd->mpending)->m_next = m;
75719370Spst		else
75819370Spst			cd->mpending = m;
75919370Spst	}
76019370Spst}
76119370Spst
76219370Spststatic bool_t
76398948Sobriensvc_vc_backchannel_recv(SVCXPRT *xprt, struct rpc_msg *msg,
76419370Spst    struct sockaddr **addrp, struct mbuf **mp)
76519370Spst{
76619370Spst	struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
76719370Spst	struct ct_data *ct;
76819370Spst	struct mbuf *m;
76919370Spst	XDR xdrs;
77019370Spst
77119370Spst	sx_xlock(&xprt->xp_lock);
77219370Spst	ct = (struct ct_data *)xprt->xp_p2;
77319370Spst	if (ct == NULL) {
77419370Spst		sx_xunlock(&xprt->xp_lock);
77519370Spst		return (FALSE);
77698948Sobrien	}
77798948Sobrien	mtx_lock(&ct->ct_lock);
77898948Sobrien	m = cd->mreq;
77998948Sobrien	if (m == NULL) {
78098948Sobrien		xprt_inactive_self(xprt);
78198948Sobrien		mtx_unlock(&ct->ct_lock);
78298948Sobrien		sx_xunlock(&xprt->xp_lock);
783130809Smarcel		return (FALSE);
784130809Smarcel	}
78598948Sobrien	cd->mreq = m->m_nextpkt;
78619370Spst	mtx_unlock(&ct->ct_lock);
78798948Sobrien	sx_xunlock(&xprt->xp_lock);
78898948Sobrien
78998948Sobrien	xdrmbuf_create(&xdrs, m, XDR_DECODE);
79098948Sobrien	if (! xdr_callmsg(&xdrs, msg)) {
79198948Sobrien		XDR_DESTROY(&xdrs);
79298948Sobrien		return (FALSE);
79398948Sobrien	}
79498948Sobrien	*addrp = NULL;
79519370Spst	*mp = xdrmbuf_getall(&xdrs);
79698948Sobrien	XDR_DESTROY(&xdrs);
79798948Sobrien	return (TRUE);
79898948Sobrien}
79998948Sobrien
80098948Sobrienstatic bool_t
80198948Sobriensvc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg,
80298948Sobrien    struct sockaddr *addr, struct mbuf *m, uint32_t *seq)
80398948Sobrien{
80498948Sobrien	XDR xdrs;
80519370Spst	struct mbuf *mrep;
80646283Sdfr	bool_t stat = TRUE;
80746283Sdfr	int error, len;
80846283Sdfr
80998948Sobrien	/*
81098948Sobrien	 * Leave space for record mark.
81198948Sobrien	 */
81298948Sobrien	MGETHDR(mrep, M_WAIT, MT_DATA);
81398948Sobrien	mrep->m_len = 0;
81498948Sobrien	mrep->m_data += sizeof(uint32_t);
81598948Sobrien
81698948Sobrien	xdrmbuf_create(&xdrs, mrep, XDR_ENCODE);
81798948Sobrien
81898948Sobrien	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
81998948Sobrien	    msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
820130809Smarcel		if (!xdr_replymsg(&xdrs, msg))
82198948Sobrien			stat = FALSE;
82219370Spst		else
82319370Spst			xdrmbuf_append(&xdrs, m);
82419370Spst	} else {
82519370Spst		stat = xdr_replymsg(&xdrs, msg);
82619370Spst	}
82719370Spst
82819370Spst	if (stat) {
82998948Sobrien		m_fixhdr(mrep);
83019370Spst
83119370Spst		/*
83219370Spst		 * Prepend a record marker containing the reply length.
83398948Sobrien		 */
83419370Spst		M_PREPEND(mrep, sizeof(uint32_t), M_WAIT);
83519370Spst		len = mrep->m_pkthdr.len;
83698948Sobrien		*mtod(mrep, uint32_t *) =
83798948Sobrien			htonl(0x80000000 | (len - sizeof(uint32_t)));
83898948Sobrien		atomic_add_acq_32(&xprt->xp_snd_cnt, len);
83998948Sobrien		error = sosend(xprt->xp_socket, NULL, NULL, mrep, NULL,
84098948Sobrien		    0, curthread);
84198948Sobrien		if (!error) {
84298948Sobrien			atomic_add_rel_32(&xprt->xp_snt_cnt, len);
84398948Sobrien			if (seq)
84498948Sobrien				*seq = xprt->xp_snd_cnt;
84598948Sobrien			stat = TRUE;
84698948Sobrien		} else
84798948Sobrien			atomic_subtract_32(&xprt->xp_snd_cnt, len);
84819370Spst	} else {
84998948Sobrien		m_freem(mrep);
85098948Sobrien	}
85198948Sobrien
85298948Sobrien	XDR_DESTROY(&xdrs);
85398948Sobrien	xprt->xp_p2 = NULL;
85498948Sobrien
85598948Sobrien	return (stat);
85698948Sobrien}
85798948Sobrien
85898948Sobrienstatic bool_t
85919370Spstsvc_vc_backchannel_reply(SVCXPRT *xprt, struct rpc_msg *msg,
86098948Sobrien    struct sockaddr *addr, struct mbuf *m, uint32_t *seq)
86198948Sobrien{
86219370Spst	struct ct_data *ct;
86398948Sobrien	XDR xdrs;
86419370Spst	struct mbuf *mrep;
86598948Sobrien	bool_t stat = TRUE;
86698948Sobrien	int error;
86798948Sobrien
86898948Sobrien	/*
86998948Sobrien	 * Leave space for record mark.
87098948Sobrien	 */
87119370Spst	MGETHDR(mrep, M_WAITOK, MT_DATA);
87219370Spst	mrep->m_len = 0;
87398948Sobrien	mrep->m_data += sizeof(uint32_t);
87498948Sobrien
87598948Sobrien	xdrmbuf_create(&xdrs, mrep, XDR_ENCODE);
87698948Sobrien
87798948Sobrien	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
87898948Sobrien	    msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
87998948Sobrien		if (!xdr_replymsg(&xdrs, msg))
88098948Sobrien			stat = FALSE;
881130809Smarcel		else
882130809Smarcel			xdrmbuf_append(&xdrs, m);
883130809Smarcel	} else {
884130809Smarcel		stat = xdr_replymsg(&xdrs, msg);
885130809Smarcel	}
886130809Smarcel
887130809Smarcel	if (stat) {
888130809Smarcel		m_fixhdr(mrep);
88998948Sobrien
89098948Sobrien		/*
89198948Sobrien		 * Prepend a record marker containing the reply length.
89298948Sobrien		 */
893130809Smarcel		M_PREPEND(mrep, sizeof(uint32_t), M_WAITOK);
894130809Smarcel		*mtod(mrep, uint32_t *) =
895130809Smarcel			htonl(0x80000000 | (mrep->m_pkthdr.len
896130809Smarcel				- sizeof(uint32_t)));
897130809Smarcel		sx_xlock(&xprt->xp_lock);
89898948Sobrien		ct = (struct ct_data *)xprt->xp_p2;
89919370Spst		if (ct != NULL)
90098948Sobrien			error = sosend(ct->ct_socket, NULL, NULL, mrep, NULL,
90119370Spst			    0, curthread);
90298948Sobrien		else
90398948Sobrien			error = EPIPE;
90498948Sobrien		sx_xunlock(&xprt->xp_lock);
90598948Sobrien		if (!error) {
90698948Sobrien			stat = TRUE;
90719370Spst		}
90898948Sobrien	} else {
90919370Spst		m_freem(mrep);
91098948Sobrien	}
91198948Sobrien
91298948Sobrien	XDR_DESTROY(&xdrs);
91319370Spst
91498948Sobrien	return (stat);
91519370Spst}
91698948Sobrien
91798948Sobrienstatic bool_t
91898948Sobriensvc_vc_null()
91919370Spst{
92098948Sobrien
92198948Sobrien	return (FALSE);
92298948Sobrien}
92398948Sobrien
92498948Sobrienstatic int
92598948Sobriensvc_vc_soupcall(struct socket *so, void *arg, int waitflag)
92698948Sobrien{
92798948Sobrien	SVCXPRT *xprt = (SVCXPRT *) arg;
928130809Smarcel
92998948Sobrien	if (soreadable(xprt->xp_socket))
93098948Sobrien		xprt_active(xprt);
93198948Sobrien	return (SU_OK);
93298948Sobrien}
93398948Sobrien
93498948Sobrien#if 0
93598948Sobrien/*
93698948Sobrien * Get the effective UID of the sending process. Used by rpcbind, keyserv
93798948Sobrien * and rpc.yppasswdd on AF_LOCAL.
93898948Sobrien */
93998948Sobrienint
94098948Sobrien__rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) {
94198948Sobrien	int sock, ret;
94298948Sobrien	gid_t egid;
94398948Sobrien	uid_t euid;
94498948Sobrien	struct sockaddr *sa;
945130809Smarcel
94698948Sobrien	sock = transp->xp_fd;
94798948Sobrien	sa = (struct sockaddr *)transp->xp_rtaddr;
94898948Sobrien	if (sa->sa_family == AF_LOCAL) {
94998948Sobrien		ret = getpeereid(sock, &euid, &egid);
95098948Sobrien		if (ret == 0)
95198948Sobrien			*uid = euid;
95298948Sobrien		return (ret);
953130809Smarcel	} else
954130809Smarcel		return (-1);
95598948Sobrien}
95698948Sobrien#endif
95798948Sobrien