svc_vc.c revision 248195
1177633Sdfr/*	$NetBSD: svc_vc.c,v 1.7 2000/08/03 00:01:53 fvdl Exp $	*/
2177633Sdfr
3177633Sdfr/*
4177633Sdfr * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5177633Sdfr * unrestricted use provided that this legend is included on all tape
6177633Sdfr * media and as a part of the software program in whole or part.  Users
7177633Sdfr * may copy or modify Sun RPC without charge, but are not authorized
8177633Sdfr * to license or distribute it to anyone else except as part of a product or
9177633Sdfr * program developed by the user.
10177633Sdfr *
11177633Sdfr * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12177633Sdfr * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13177633Sdfr * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14177633Sdfr *
15177633Sdfr * Sun RPC is provided with no support and without any obligation on the
16177633Sdfr * part of Sun Microsystems, Inc. to assist in its use, correction,
17177633Sdfr * modification or enhancement.
18177633Sdfr *
19177633Sdfr * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20177633Sdfr * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21177633Sdfr * OR ANY PART THEREOF.
22177633Sdfr *
23177633Sdfr * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24177633Sdfr * or profits or other special, indirect and consequential damages, even if
25177633Sdfr * Sun has been advised of the possibility of such damages.
26177633Sdfr *
27177633Sdfr * Sun Microsystems, Inc.
28177633Sdfr * 2550 Garcia Avenue
29177633Sdfr * Mountain View, California  94043
30177633Sdfr */
31177633Sdfr
32177633Sdfr#if defined(LIBC_SCCS) && !defined(lint)
33177633Sdfrstatic char *sccsid2 = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
34177633Sdfrstatic char *sccsid = "@(#)svc_tcp.c	2.2 88/08/01 4.0 RPCSRC";
35177633Sdfr#endif
36177633Sdfr#include <sys/cdefs.h>
37177633Sdfr__FBSDID("$FreeBSD: head/sys/rpc/svc_vc.c 248195 2013-03-12 12:17:19Z glebius $");
38177633Sdfr
39177633Sdfr/*
40177633Sdfr * svc_vc.c, Server side for Connection Oriented based RPC.
41177633Sdfr *
42177633Sdfr * Actually implements two flavors of transporter -
43177633Sdfr * a tcp rendezvouser (a listner and connection establisher)
44177633Sdfr * and a record/tcp stream.
45177633Sdfr */
46177633Sdfr
47177633Sdfr#include <sys/param.h>
48177633Sdfr#include <sys/lock.h>
49177633Sdfr#include <sys/kernel.h>
50177633Sdfr#include <sys/malloc.h>
51177633Sdfr#include <sys/mbuf.h>
52177633Sdfr#include <sys/mutex.h>
53193509Srwatson#include <sys/proc.h>
54177633Sdfr#include <sys/protosw.h>
55177633Sdfr#include <sys/queue.h>
56177633Sdfr#include <sys/socket.h>
57177633Sdfr#include <sys/socketvar.h>
58184588Sdfr#include <sys/sx.h>
59177633Sdfr#include <sys/systm.h>
60177633Sdfr#include <sys/uio.h>
61196503Szec
62196503Szec#include <net/vnet.h>
63196503Szec
64177633Sdfr#include <netinet/tcp.h>
65177633Sdfr
66177633Sdfr#include <rpc/rpc.h>
67177633Sdfr
68244008Srmacklem#include <rpc/krpc.h>
69177685Sdfr#include <rpc/rpc_com.h>
70177633Sdfr
71193509Srwatson#include <security/mac/mac_framework.h>
72193509Srwatson
73184588Sdfrstatic bool_t svc_vc_rendezvous_recv(SVCXPRT *, struct rpc_msg *,
74184588Sdfr    struct sockaddr **, struct mbuf **);
75177633Sdfrstatic enum xprt_stat svc_vc_rendezvous_stat(SVCXPRT *);
76177633Sdfrstatic void svc_vc_rendezvous_destroy(SVCXPRT *);
77177633Sdfrstatic bool_t svc_vc_null(void);
78177633Sdfrstatic void svc_vc_destroy(SVCXPRT *);
79177633Sdfrstatic enum xprt_stat svc_vc_stat(SVCXPRT *);
80184588Sdfrstatic bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *,
81184588Sdfr    struct sockaddr **, struct mbuf **);
82184588Sdfrstatic bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *,
83184588Sdfr    struct sockaddr *, struct mbuf *);
84177633Sdfrstatic bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in);
85177633Sdfrstatic bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq,
86177633Sdfr    void *in);
87244008Srmacklemstatic void svc_vc_backchannel_destroy(SVCXPRT *);
88244008Srmacklemstatic enum xprt_stat svc_vc_backchannel_stat(SVCXPRT *);
89244008Srmacklemstatic bool_t svc_vc_backchannel_recv(SVCXPRT *, struct rpc_msg *,
90244008Srmacklem    struct sockaddr **, struct mbuf **);
91244008Srmacklemstatic bool_t svc_vc_backchannel_reply(SVCXPRT *, struct rpc_msg *,
92244008Srmacklem    struct sockaddr *, struct mbuf *);
93244008Srmacklemstatic bool_t svc_vc_backchannel_control(SVCXPRT *xprt, const u_int rq,
94244008Srmacklem    void *in);
95177633Sdfrstatic SVCXPRT *svc_vc_create_conn(SVCPOOL *pool, struct socket *so,
96177633Sdfr    struct sockaddr *raddr);
97177633Sdfrstatic int svc_vc_accept(struct socket *head, struct socket **sop);
98193272Sjhbstatic int svc_vc_soupcall(struct socket *so, void *arg, int waitflag);
99177633Sdfr
100177633Sdfrstatic struct xp_ops svc_vc_rendezvous_ops = {
101177633Sdfr	.xp_recv =	svc_vc_rendezvous_recv,
102177633Sdfr	.xp_stat =	svc_vc_rendezvous_stat,
103184588Sdfr	.xp_reply =	(bool_t (*)(SVCXPRT *, struct rpc_msg *,
104184588Sdfr		struct sockaddr *, struct mbuf *))svc_vc_null,
105177633Sdfr	.xp_destroy =	svc_vc_rendezvous_destroy,
106177633Sdfr	.xp_control =	svc_vc_rendezvous_control
107177633Sdfr};
108177633Sdfr
109177633Sdfrstatic struct xp_ops svc_vc_ops = {
110177633Sdfr	.xp_recv =	svc_vc_recv,
111177633Sdfr	.xp_stat =	svc_vc_stat,
112177633Sdfr	.xp_reply =	svc_vc_reply,
113177633Sdfr	.xp_destroy =	svc_vc_destroy,
114177633Sdfr	.xp_control =	svc_vc_control
115177633Sdfr};
116177633Sdfr
117244008Srmacklemstatic struct xp_ops svc_vc_backchannel_ops = {
118244008Srmacklem	.xp_recv =	svc_vc_backchannel_recv,
119244008Srmacklem	.xp_stat =	svc_vc_backchannel_stat,
120244008Srmacklem	.xp_reply =	svc_vc_backchannel_reply,
121244008Srmacklem	.xp_destroy =	svc_vc_backchannel_destroy,
122244008Srmacklem	.xp_control =	svc_vc_backchannel_control
123177633Sdfr};
124177633Sdfr
125177633Sdfr/*
126177633Sdfr * Usage:
127177633Sdfr *	xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
128177633Sdfr *
129177633Sdfr * Creates, registers, and returns a (rpc) tcp based transporter.
130177633Sdfr * Once *xprt is initialized, it is registered as a transporter
131177633Sdfr * see (svc.h, xprt_register).  This routine returns
132177633Sdfr * a NULL if a problem occurred.
133177633Sdfr *
134177633Sdfr * The filedescriptor passed in is expected to refer to a bound, but
135177633Sdfr * not yet connected socket.
136177633Sdfr *
137177633Sdfr * Since streams do buffered io similar to stdio, the caller can specify
138177633Sdfr * how big the send and receive buffers are via the second and third parms;
139177633Sdfr * 0 => use the system default.
140177633Sdfr */
141177633SdfrSVCXPRT *
142177633Sdfrsvc_vc_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
143177633Sdfr    size_t recvsize)
144177633Sdfr{
145177633Sdfr	SVCXPRT *xprt;
146177633Sdfr	struct sockaddr* sa;
147177633Sdfr	int error;
148177633Sdfr
149180025Sdfr	if (so->so_state & SS_ISCONNECTED) {
150180025Sdfr		error = so->so_proto->pr_usrreqs->pru_peeraddr(so, &sa);
151180025Sdfr		if (error)
152180025Sdfr			return (NULL);
153180025Sdfr		xprt = svc_vc_create_conn(pool, so, sa);
154180025Sdfr		free(sa, M_SONAME);
155180025Sdfr		return (xprt);
156180025Sdfr	}
157180025Sdfr
158184588Sdfr	xprt = svc_xprt_alloc();
159184588Sdfr	sx_init(&xprt->xp_lock, "xprt->xp_lock");
160177633Sdfr	xprt->xp_pool = pool;
161177633Sdfr	xprt->xp_socket = so;
162177633Sdfr	xprt->xp_p1 = NULL;
163177633Sdfr	xprt->xp_p2 = NULL;
164177633Sdfr	xprt->xp_ops = &svc_vc_rendezvous_ops;
165177633Sdfr
166177633Sdfr	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
167196503Szec	if (error) {
168177633Sdfr		goto cleanup_svc_vc_create;
169196503Szec	}
170177633Sdfr
171184588Sdfr	memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
172177633Sdfr	free(sa, M_SONAME);
173177633Sdfr
174177633Sdfr	xprt_register(xprt);
175177633Sdfr
176177633Sdfr	solisten(so, SOMAXCONN, curthread);
177177633Sdfr
178177633Sdfr	SOCKBUF_LOCK(&so->so_rcv);
179193436Srmacklem	xprt->xp_upcallset = 1;
180193272Sjhb	soupcall_set(so, SO_RCV, svc_vc_soupcall, xprt);
181177633Sdfr	SOCKBUF_UNLOCK(&so->so_rcv);
182177633Sdfr
183177633Sdfr	return (xprt);
184177633Sdfrcleanup_svc_vc_create:
185177633Sdfr	if (xprt)
186184588Sdfr		svc_xprt_free(xprt);
187177633Sdfr	return (NULL);
188177633Sdfr}
189177633Sdfr
190177633Sdfr/*
191177633Sdfr * Create a new transport for a socket optained via soaccept().
192177633Sdfr */
193177633SdfrSVCXPRT *
194177633Sdfrsvc_vc_create_conn(SVCPOOL *pool, struct socket *so, struct sockaddr *raddr)
195177633Sdfr{
196177633Sdfr	SVCXPRT *xprt = NULL;
197177633Sdfr	struct cf_conn *cd = NULL;
198177633Sdfr	struct sockaddr* sa = NULL;
199180025Sdfr	struct sockopt opt;
200180025Sdfr	int one = 1;
201177633Sdfr	int error;
202177633Sdfr
203180025Sdfr	bzero(&opt, sizeof(struct sockopt));
204180025Sdfr	opt.sopt_dir = SOPT_SET;
205180025Sdfr	opt.sopt_level = SOL_SOCKET;
206180025Sdfr	opt.sopt_name = SO_KEEPALIVE;
207180025Sdfr	opt.sopt_val = &one;
208180025Sdfr	opt.sopt_valsize = sizeof(one);
209180025Sdfr	error = sosetopt(so, &opt);
210196503Szec	if (error) {
211180025Sdfr		return (NULL);
212196503Szec	}
213180025Sdfr
214180025Sdfr	if (so->so_proto->pr_protocol == IPPROTO_TCP) {
215180025Sdfr		bzero(&opt, sizeof(struct sockopt));
216180025Sdfr		opt.sopt_dir = SOPT_SET;
217180025Sdfr		opt.sopt_level = IPPROTO_TCP;
218180025Sdfr		opt.sopt_name = TCP_NODELAY;
219180025Sdfr		opt.sopt_val = &one;
220180025Sdfr		opt.sopt_valsize = sizeof(one);
221180025Sdfr		error = sosetopt(so, &opt);
222196503Szec		if (error) {
223180025Sdfr			return (NULL);
224196503Szec		}
225180025Sdfr	}
226180025Sdfr
227177633Sdfr	cd = mem_alloc(sizeof(*cd));
228177633Sdfr	cd->strm_stat = XPRT_IDLE;
229177633Sdfr
230184588Sdfr	xprt = svc_xprt_alloc();
231184588Sdfr	sx_init(&xprt->xp_lock, "xprt->xp_lock");
232177633Sdfr	xprt->xp_pool = pool;
233177633Sdfr	xprt->xp_socket = so;
234177633Sdfr	xprt->xp_p1 = cd;
235177633Sdfr	xprt->xp_p2 = NULL;
236177633Sdfr	xprt->xp_ops = &svc_vc_ops;
237177633Sdfr
238184588Sdfr	/*
239184588Sdfr	 * See http://www.connectathon.org/talks96/nfstcp.pdf - client
240184588Sdfr	 * has a 5 minute timer, server has a 6 minute timer.
241184588Sdfr	 */
242184588Sdfr	xprt->xp_idletimeout = 6 * 60;
243177633Sdfr
244184588Sdfr	memcpy(&xprt->xp_rtaddr, raddr, raddr->sa_len);
245184588Sdfr
246177633Sdfr	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
247177633Sdfr	if (error)
248177633Sdfr		goto cleanup_svc_vc_create;
249177633Sdfr
250184588Sdfr	memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
251177633Sdfr	free(sa, M_SONAME);
252177633Sdfr
253177633Sdfr	xprt_register(xprt);
254177633Sdfr
255177633Sdfr	SOCKBUF_LOCK(&so->so_rcv);
256193436Srmacklem	xprt->xp_upcallset = 1;
257193272Sjhb	soupcall_set(so, SO_RCV, svc_vc_soupcall, xprt);
258177633Sdfr	SOCKBUF_UNLOCK(&so->so_rcv);
259177633Sdfr
260177633Sdfr	/*
261177633Sdfr	 * Throw the transport into the active list in case it already
262177633Sdfr	 * has some data buffered.
263177633Sdfr	 */
264184588Sdfr	sx_xlock(&xprt->xp_lock);
265177633Sdfr	xprt_active(xprt);
266184588Sdfr	sx_xunlock(&xprt->xp_lock);
267177633Sdfr
268177633Sdfr	return (xprt);
269177633Sdfrcleanup_svc_vc_create:
270177633Sdfr	if (xprt) {
271177633Sdfr		mem_free(xprt, sizeof(*xprt));
272177633Sdfr	}
273177633Sdfr	if (cd)
274177633Sdfr		mem_free(cd, sizeof(*cd));
275177633Sdfr	return (NULL);
276177633Sdfr}
277177633Sdfr
278177633Sdfr/*
279244008Srmacklem * Create a new transport for a backchannel on a clnt_vc socket.
280244008Srmacklem */
281244008SrmacklemSVCXPRT *
282244008Srmacklemsvc_vc_create_backchannel(SVCPOOL *pool)
283244008Srmacklem{
284244008Srmacklem	SVCXPRT *xprt = NULL;
285244008Srmacklem	struct cf_conn *cd = NULL;
286244008Srmacklem
287244008Srmacklem	cd = mem_alloc(sizeof(*cd));
288244008Srmacklem	cd->strm_stat = XPRT_IDLE;
289244008Srmacklem
290244008Srmacklem	xprt = svc_xprt_alloc();
291244008Srmacklem	sx_init(&xprt->xp_lock, "xprt->xp_lock");
292244008Srmacklem	xprt->xp_pool = pool;
293244008Srmacklem	xprt->xp_socket = NULL;
294244008Srmacklem	xprt->xp_p1 = cd;
295244008Srmacklem	xprt->xp_p2 = NULL;
296244008Srmacklem	xprt->xp_ops = &svc_vc_backchannel_ops;
297244008Srmacklem	return (xprt);
298244008Srmacklem}
299244008Srmacklem
300244008Srmacklem/*
301177633Sdfr * This does all of the accept except the final call to soaccept. The
302177633Sdfr * caller will call soaccept after dropping its locks (soaccept may
303177633Sdfr * call malloc).
304177633Sdfr */
305177633Sdfrint
306177633Sdfrsvc_vc_accept(struct socket *head, struct socket **sop)
307177633Sdfr{
308177633Sdfr	int error = 0;
309177633Sdfr	struct socket *so;
310177633Sdfr
311177633Sdfr	if ((head->so_options & SO_ACCEPTCONN) == 0) {
312177633Sdfr		error = EINVAL;
313177633Sdfr		goto done;
314177633Sdfr	}
315177633Sdfr#ifdef MAC
316193509Srwatson	error = mac_socket_check_accept(curthread->td_ucred, head);
317177633Sdfr	if (error != 0)
318177633Sdfr		goto done;
319177633Sdfr#endif
320177633Sdfr	ACCEPT_LOCK();
321177633Sdfr	if (TAILQ_EMPTY(&head->so_comp)) {
322177633Sdfr		ACCEPT_UNLOCK();
323177633Sdfr		error = EWOULDBLOCK;
324177633Sdfr		goto done;
325177633Sdfr	}
326177633Sdfr	so = TAILQ_FIRST(&head->so_comp);
327177633Sdfr	KASSERT(!(so->so_qstate & SQ_INCOMP), ("svc_vc_accept: so SQ_INCOMP"));
328177633Sdfr	KASSERT(so->so_qstate & SQ_COMP, ("svc_vc_accept: so not SQ_COMP"));
329177633Sdfr
330177633Sdfr	/*
331177633Sdfr	 * Before changing the flags on the socket, we have to bump the
332177633Sdfr	 * reference count.  Otherwise, if the protocol calls sofree(),
333177633Sdfr	 * the socket will be released due to a zero refcount.
334177633Sdfr	 * XXX might not need soref() since this is simpler than kern_accept.
335177633Sdfr	 */
336177633Sdfr	SOCK_LOCK(so);			/* soref() and so_state update */
337177633Sdfr	soref(so);			/* file descriptor reference */
338177633Sdfr
339177633Sdfr	TAILQ_REMOVE(&head->so_comp, so, so_list);
340177633Sdfr	head->so_qlen--;
341177633Sdfr	so->so_state |= (head->so_state & SS_NBIO);
342177633Sdfr	so->so_qstate &= ~SQ_COMP;
343177633Sdfr	so->so_head = NULL;
344177633Sdfr
345177633Sdfr	SOCK_UNLOCK(so);
346177633Sdfr	ACCEPT_UNLOCK();
347177633Sdfr
348177633Sdfr	*sop = so;
349177633Sdfr
350177633Sdfr	/* connection has been removed from the listen queue */
351177633Sdfr	KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0);
352177633Sdfrdone:
353177633Sdfr	return (error);
354177633Sdfr}
355177633Sdfr
356177633Sdfr/*ARGSUSED*/
357177633Sdfrstatic bool_t
358184588Sdfrsvc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg,
359184588Sdfr    struct sockaddr **addrp, struct mbuf **mp)
360177633Sdfr{
361177633Sdfr	struct socket *so = NULL;
362177633Sdfr	struct sockaddr *sa = NULL;
363177633Sdfr	int error;
364194407Srmacklem	SVCXPRT *new_xprt;
365177633Sdfr
366177633Sdfr	/*
367177633Sdfr	 * The socket upcall calls xprt_active() which will eventually
368177633Sdfr	 * cause the server to call us here. We attempt to accept a
369177633Sdfr	 * connection from the socket and turn it into a new
370177633Sdfr	 * transport. If the accept fails, we have drained all pending
371177633Sdfr	 * connections so we call xprt_inactive().
372177633Sdfr	 */
373184588Sdfr	sx_xlock(&xprt->xp_lock);
374177633Sdfr
375177633Sdfr	error = svc_vc_accept(xprt->xp_socket, &so);
376177633Sdfr
377177633Sdfr	if (error == EWOULDBLOCK) {
378184588Sdfr		/*
379184588Sdfr		 * We must re-test for new connections after taking
380184588Sdfr		 * the lock to protect us in the case where a new
381184588Sdfr		 * connection arrives after our call to accept fails
382184588Sdfr		 * with EWOULDBLOCK. The pool lock protects us from
383184588Sdfr		 * racing the upcall after our TAILQ_EMPTY() call
384184588Sdfr		 * returns false.
385184588Sdfr		 */
386184588Sdfr		ACCEPT_LOCK();
387184588Sdfr		mtx_lock(&xprt->xp_pool->sp_lock);
388184588Sdfr		if (TAILQ_EMPTY(&xprt->xp_socket->so_comp))
389184588Sdfr			xprt_inactive_locked(xprt);
390184588Sdfr		mtx_unlock(&xprt->xp_pool->sp_lock);
391184588Sdfr		ACCEPT_UNLOCK();
392184588Sdfr		sx_xunlock(&xprt->xp_lock);
393177633Sdfr		return (FALSE);
394177633Sdfr	}
395177633Sdfr
396177633Sdfr	if (error) {
397177633Sdfr		SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
398193436Srmacklem		if (xprt->xp_upcallset) {
399193436Srmacklem			xprt->xp_upcallset = 0;
400193436Srmacklem			soupcall_clear(xprt->xp_socket, SO_RCV);
401193436Srmacklem		}
402177633Sdfr		SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
403177633Sdfr		xprt_inactive(xprt);
404184588Sdfr		sx_xunlock(&xprt->xp_lock);
405177633Sdfr		return (FALSE);
406177633Sdfr	}
407177633Sdfr
408184588Sdfr	sx_xunlock(&xprt->xp_lock);
409177633Sdfr
410177633Sdfr	sa = 0;
411177633Sdfr	error = soaccept(so, &sa);
412177633Sdfr
413177633Sdfr	if (error) {
414177633Sdfr		/*
415177633Sdfr		 * XXX not sure if I need to call sofree or soclose here.
416177633Sdfr		 */
417177633Sdfr		if (sa)
418177633Sdfr			free(sa, M_SONAME);
419177633Sdfr		return (FALSE);
420177633Sdfr	}
421177633Sdfr
422177633Sdfr	/*
423177633Sdfr	 * svc_vc_create_conn will call xprt_register - we don't need
424194407Srmacklem	 * to do anything with the new connection except derefence it.
425177633Sdfr	 */
426194407Srmacklem	new_xprt = svc_vc_create_conn(xprt->xp_pool, so, sa);
427194407Srmacklem	if (!new_xprt) {
428180025Sdfr		soclose(so);
429194407Srmacklem	} else {
430194407Srmacklem		SVC_RELEASE(new_xprt);
431194407Srmacklem	}
432180025Sdfr
433177633Sdfr	free(sa, M_SONAME);
434177633Sdfr
435177633Sdfr	return (FALSE); /* there is never an rpc msg to be processed */
436177633Sdfr}
437177633Sdfr
438177633Sdfr/*ARGSUSED*/
439177633Sdfrstatic enum xprt_stat
440177633Sdfrsvc_vc_rendezvous_stat(SVCXPRT *xprt)
441177633Sdfr{
442177633Sdfr
443177633Sdfr	return (XPRT_IDLE);
444177633Sdfr}
445177633Sdfr
446177633Sdfrstatic void
447177633Sdfrsvc_vc_destroy_common(SVCXPRT *xprt)
448177633Sdfr{
449177633Sdfr	SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
450193436Srmacklem	if (xprt->xp_upcallset) {
451193436Srmacklem		xprt->xp_upcallset = 0;
452193436Srmacklem		soupcall_clear(xprt->xp_socket, SO_RCV);
453193436Srmacklem	}
454177633Sdfr	SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
455177633Sdfr
456184588Sdfr	sx_destroy(&xprt->xp_lock);
457177633Sdfr	if (xprt->xp_socket)
458177633Sdfr		(void)soclose(xprt->xp_socket);
459177633Sdfr
460184588Sdfr	if (xprt->xp_netid)
461184588Sdfr		(void) mem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1);
462184588Sdfr	svc_xprt_free(xprt);
463177633Sdfr}
464177633Sdfr
465177633Sdfrstatic void
466177633Sdfrsvc_vc_rendezvous_destroy(SVCXPRT *xprt)
467177633Sdfr{
468177633Sdfr
469177633Sdfr	svc_vc_destroy_common(xprt);
470177633Sdfr}
471177633Sdfr
472177633Sdfrstatic void
473177633Sdfrsvc_vc_destroy(SVCXPRT *xprt)
474177633Sdfr{
475177633Sdfr	struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1;
476177633Sdfr
477177633Sdfr	svc_vc_destroy_common(xprt);
478177633Sdfr
479177633Sdfr	if (cd->mreq)
480177633Sdfr		m_freem(cd->mreq);
481177633Sdfr	if (cd->mpending)
482177633Sdfr		m_freem(cd->mpending);
483177633Sdfr	mem_free(cd, sizeof(*cd));
484177633Sdfr}
485177633Sdfr
486244008Srmacklemstatic void
487244008Srmacklemsvc_vc_backchannel_destroy(SVCXPRT *xprt)
488244008Srmacklem{
489244008Srmacklem	struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1;
490244008Srmacklem	struct mbuf *m, *m2;
491244008Srmacklem
492244008Srmacklem	svc_xprt_free(xprt);
493244008Srmacklem	m = cd->mreq;
494244008Srmacklem	while (m != NULL) {
495244008Srmacklem		m2 = m;
496244008Srmacklem		m = m->m_nextpkt;
497244008Srmacklem		m_freem(m2);
498244008Srmacklem	}
499244008Srmacklem	mem_free(cd, sizeof(*cd));
500244008Srmacklem}
501244008Srmacklem
502177633Sdfr/*ARGSUSED*/
503177633Sdfrstatic bool_t
504177633Sdfrsvc_vc_control(SVCXPRT *xprt, const u_int rq, void *in)
505177633Sdfr{
506177633Sdfr	return (FALSE);
507177633Sdfr}
508177633Sdfr
509177633Sdfrstatic bool_t
510177633Sdfrsvc_vc_rendezvous_control(SVCXPRT *xprt, const u_int rq, void *in)
511177633Sdfr{
512177633Sdfr
513177633Sdfr	return (FALSE);
514177633Sdfr}
515177633Sdfr
516244008Srmacklemstatic bool_t
517244008Srmacklemsvc_vc_backchannel_control(SVCXPRT *xprt, const u_int rq, void *in)
518244008Srmacklem{
519244008Srmacklem
520244008Srmacklem	return (FALSE);
521244008Srmacklem}
522244008Srmacklem
523177633Sdfrstatic enum xprt_stat
524177633Sdfrsvc_vc_stat(SVCXPRT *xprt)
525177633Sdfr{
526177633Sdfr	struct cf_conn *cd;
527177633Sdfr	struct mbuf *m;
528177633Sdfr	size_t n;
529177633Sdfr
530177633Sdfr	cd = (struct cf_conn *)(xprt->xp_p1);
531177633Sdfr
532177633Sdfr	if (cd->strm_stat == XPRT_DIED)
533177633Sdfr		return (XPRT_DIED);
534177633Sdfr
535177633Sdfr	/*
536177633Sdfr	 * Return XPRT_MOREREQS if we have buffered data and we are
537184588Sdfr	 * mid-record or if we have enough data for a record
538184588Sdfr	 * marker. Since this is only a hint, we read mpending and
539184588Sdfr	 * resid outside the lock. We do need to take the lock if we
540184588Sdfr	 * have to traverse the mbuf chain.
541177633Sdfr	 */
542177633Sdfr	if (cd->mpending) {
543177633Sdfr		if (cd->resid)
544177633Sdfr			return (XPRT_MOREREQS);
545177633Sdfr		n = 0;
546184588Sdfr		sx_xlock(&xprt->xp_lock);
547177633Sdfr		m = cd->mpending;
548177633Sdfr		while (m && n < sizeof(uint32_t)) {
549177633Sdfr			n += m->m_len;
550177633Sdfr			m = m->m_next;
551177633Sdfr		}
552184588Sdfr		sx_xunlock(&xprt->xp_lock);
553177633Sdfr		if (n >= sizeof(uint32_t))
554177633Sdfr			return (XPRT_MOREREQS);
555177633Sdfr	}
556177633Sdfr
557184588Sdfr	if (soreadable(xprt->xp_socket))
558184588Sdfr		return (XPRT_MOREREQS);
559184588Sdfr
560177633Sdfr	return (XPRT_IDLE);
561177633Sdfr}
562177633Sdfr
563244008Srmacklemstatic enum xprt_stat
564244008Srmacklemsvc_vc_backchannel_stat(SVCXPRT *xprt)
565244008Srmacklem{
566244008Srmacklem	struct cf_conn *cd;
567244008Srmacklem
568244008Srmacklem	cd = (struct cf_conn *)(xprt->xp_p1);
569244008Srmacklem
570244008Srmacklem	if (cd->mreq != NULL)
571244008Srmacklem		return (XPRT_MOREREQS);
572244008Srmacklem
573244008Srmacklem	return (XPRT_IDLE);
574244008Srmacklem}
575244008Srmacklem
576177633Sdfrstatic bool_t
577184588Sdfrsvc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
578184588Sdfr    struct sockaddr **addrp, struct mbuf **mp)
579177633Sdfr{
580177633Sdfr	struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
581177633Sdfr	struct uio uio;
582177633Sdfr	struct mbuf *m;
583184588Sdfr	XDR xdrs;
584177633Sdfr	int error, rcvflag;
585177633Sdfr
586184588Sdfr	/*
587184588Sdfr	 * Serialise access to the socket and our own record parsing
588184588Sdfr	 * state.
589184588Sdfr	 */
590184588Sdfr	sx_xlock(&xprt->xp_lock);
591184588Sdfr
592177633Sdfr	for (;;) {
593177633Sdfr		/*
594177633Sdfr		 * If we have an mbuf chain in cd->mpending, try to parse a
595177633Sdfr		 * record from it, leaving the result in cd->mreq. If we don't
596177633Sdfr		 * have a complete record, leave the partial result in
597177633Sdfr		 * cd->mreq and try to read more from the socket.
598177633Sdfr		 */
599177633Sdfr		if (cd->mpending) {
600177633Sdfr			/*
601177633Sdfr			 * If cd->resid is non-zero, we have part of the
602177633Sdfr			 * record already, otherwise we are expecting a record
603177633Sdfr			 * marker.
604177633Sdfr			 */
605177633Sdfr			if (!cd->resid) {
606177633Sdfr				/*
607177633Sdfr				 * See if there is enough data buffered to
608177633Sdfr				 * make up a record marker. Make sure we can
609177633Sdfr				 * handle the case where the record marker is
610177633Sdfr				 * split across more than one mbuf.
611177633Sdfr				 */
612177633Sdfr				size_t n = 0;
613177633Sdfr				uint32_t header;
614177633Sdfr
615177633Sdfr				m = cd->mpending;
616177633Sdfr				while (n < sizeof(uint32_t) && m) {
617177633Sdfr					n += m->m_len;
618177633Sdfr					m = m->m_next;
619177633Sdfr				}
620177633Sdfr				if (n < sizeof(uint32_t))
621177633Sdfr					goto readmore;
622217242Srmacklem				m_copydata(cd->mpending, 0, sizeof(header),
623217242Srmacklem				    (char *)&header);
624177633Sdfr				header = ntohl(header);
625177633Sdfr				cd->eor = (header & 0x80000000) != 0;
626177633Sdfr				cd->resid = header & 0x7fffffff;
627177633Sdfr				m_adj(cd->mpending, sizeof(uint32_t));
628177633Sdfr			}
629177633Sdfr
630177633Sdfr			/*
631177633Sdfr			 * Start pulling off mbufs from cd->mpending
632177633Sdfr			 * until we either have a complete record or
633177633Sdfr			 * we run out of data. We use m_split to pull
634177633Sdfr			 * data - it will pull as much as possible and
635177633Sdfr			 * split the last mbuf if necessary.
636177633Sdfr			 */
637177633Sdfr			while (cd->mpending && cd->resid) {
638177633Sdfr				m = cd->mpending;
639184588Sdfr				if (cd->mpending->m_next
640184588Sdfr				    || cd->mpending->m_len > cd->resid)
641184588Sdfr					cd->mpending = m_split(cd->mpending,
642243882Sglebius					    cd->resid, M_WAITOK);
643184588Sdfr				else
644184588Sdfr					cd->mpending = NULL;
645177633Sdfr				if (cd->mreq)
646177633Sdfr					m_last(cd->mreq)->m_next = m;
647177633Sdfr				else
648177633Sdfr					cd->mreq = m;
649177633Sdfr				while (m) {
650177633Sdfr					cd->resid -= m->m_len;
651177633Sdfr					m = m->m_next;
652177633Sdfr				}
653177633Sdfr			}
654177633Sdfr
655177633Sdfr			/*
656177633Sdfr			 * If cd->resid is zero now, we have managed to
657177633Sdfr			 * receive a record fragment from the stream. Check
658177633Sdfr			 * for the end-of-record mark to see if we need more.
659177633Sdfr			 */
660177633Sdfr			if (cd->resid == 0) {
661177633Sdfr				if (!cd->eor)
662177633Sdfr					continue;
663177633Sdfr
664177633Sdfr				/*
665177633Sdfr				 * Success - we have a complete record in
666177633Sdfr				 * cd->mreq.
667177633Sdfr				 */
668184588Sdfr				xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE);
669177633Sdfr				cd->mreq = NULL;
670184588Sdfr				sx_xunlock(&xprt->xp_lock);
671184588Sdfr
672184588Sdfr				if (! xdr_callmsg(&xdrs, msg)) {
673184588Sdfr					XDR_DESTROY(&xdrs);
674177633Sdfr					return (FALSE);
675177633Sdfr				}
676177633Sdfr
677184588Sdfr				*addrp = NULL;
678184588Sdfr				*mp = xdrmbuf_getall(&xdrs);
679184588Sdfr				XDR_DESTROY(&xdrs);
680184588Sdfr
681177633Sdfr				return (TRUE);
682177633Sdfr			}
683177633Sdfr		}
684177633Sdfr
685177633Sdfr	readmore:
686177633Sdfr		/*
687177633Sdfr		 * The socket upcall calls xprt_active() which will eventually
688177633Sdfr		 * cause the server to call us here. We attempt to
689177633Sdfr		 * read as much as possible from the socket and put
690177633Sdfr		 * the result in cd->mpending. If the read fails,
691177633Sdfr		 * we have drained both cd->mpending and the socket so
692177633Sdfr		 * we can call xprt_inactive().
693177633Sdfr		 */
694177633Sdfr		uio.uio_resid = 1000000000;
695177633Sdfr		uio.uio_td = curthread;
696177633Sdfr		m = NULL;
697177633Sdfr		rcvflag = MSG_DONTWAIT;
698177633Sdfr		error = soreceive(xprt->xp_socket, NULL, &uio, &m, NULL,
699177633Sdfr		    &rcvflag);
700177633Sdfr
701177633Sdfr		if (error == EWOULDBLOCK) {
702184588Sdfr			/*
703184588Sdfr			 * We must re-test for readability after
704184588Sdfr			 * taking the lock to protect us in the case
705184588Sdfr			 * where a new packet arrives on the socket
706184588Sdfr			 * after our call to soreceive fails with
707184588Sdfr			 * EWOULDBLOCK. The pool lock protects us from
708184588Sdfr			 * racing the upcall after our soreadable()
709184588Sdfr			 * call returns false.
710184588Sdfr			 */
711184588Sdfr			mtx_lock(&xprt->xp_pool->sp_lock);
712184588Sdfr			if (!soreadable(xprt->xp_socket))
713184588Sdfr				xprt_inactive_locked(xprt);
714184588Sdfr			mtx_unlock(&xprt->xp_pool->sp_lock);
715184588Sdfr			sx_xunlock(&xprt->xp_lock);
716177633Sdfr			return (FALSE);
717177633Sdfr		}
718177633Sdfr
719177633Sdfr		if (error) {
720177633Sdfr			SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
721193436Srmacklem			if (xprt->xp_upcallset) {
722193436Srmacklem				xprt->xp_upcallset = 0;
723193436Srmacklem				soupcall_clear(xprt->xp_socket, SO_RCV);
724193436Srmacklem			}
725177633Sdfr			SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
726177633Sdfr			xprt_inactive(xprt);
727177633Sdfr			cd->strm_stat = XPRT_DIED;
728184588Sdfr			sx_xunlock(&xprt->xp_lock);
729177633Sdfr			return (FALSE);
730177633Sdfr		}
731177633Sdfr
732177633Sdfr		if (!m) {
733177633Sdfr			/*
734177633Sdfr			 * EOF - the other end has closed the socket.
735177633Sdfr			 */
736184588Sdfr			xprt_inactive(xprt);
737177633Sdfr			cd->strm_stat = XPRT_DIED;
738184588Sdfr			sx_xunlock(&xprt->xp_lock);
739177633Sdfr			return (FALSE);
740177633Sdfr		}
741177633Sdfr
742177633Sdfr		if (cd->mpending)
743177633Sdfr			m_last(cd->mpending)->m_next = m;
744177633Sdfr		else
745177633Sdfr			cd->mpending = m;
746177633Sdfr	}
747177633Sdfr}
748177633Sdfr
749177633Sdfrstatic bool_t
750244008Srmacklemsvc_vc_backchannel_recv(SVCXPRT *xprt, struct rpc_msg *msg,
751244008Srmacklem    struct sockaddr **addrp, struct mbuf **mp)
752244008Srmacklem{
753244008Srmacklem	struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
754244008Srmacklem	struct ct_data *ct;
755244008Srmacklem	struct mbuf *m;
756244008Srmacklem	XDR xdrs;
757244008Srmacklem
758244008Srmacklem	sx_xlock(&xprt->xp_lock);
759244008Srmacklem	ct = (struct ct_data *)xprt->xp_p2;
760244008Srmacklem	if (ct == NULL) {
761244008Srmacklem		sx_xunlock(&xprt->xp_lock);
762244008Srmacklem		return (FALSE);
763244008Srmacklem	}
764244008Srmacklem	mtx_lock(&ct->ct_lock);
765244008Srmacklem	m = cd->mreq;
766244008Srmacklem	if (m == NULL) {
767244008Srmacklem		xprt_inactive(xprt);
768244008Srmacklem		mtx_unlock(&ct->ct_lock);
769244008Srmacklem		sx_xunlock(&xprt->xp_lock);
770244008Srmacklem		return (FALSE);
771244008Srmacklem	}
772244008Srmacklem	cd->mreq = m->m_nextpkt;
773244008Srmacklem	mtx_unlock(&ct->ct_lock);
774244008Srmacklem	sx_xunlock(&xprt->xp_lock);
775244008Srmacklem
776244008Srmacklem	xdrmbuf_create(&xdrs, m, XDR_DECODE);
777244008Srmacklem	if (! xdr_callmsg(&xdrs, msg)) {
778244008Srmacklem		XDR_DESTROY(&xdrs);
779244008Srmacklem		return (FALSE);
780244008Srmacklem	}
781244008Srmacklem	*addrp = NULL;
782244008Srmacklem	*mp = xdrmbuf_getall(&xdrs);
783244008Srmacklem	XDR_DESTROY(&xdrs);
784244008Srmacklem	return (TRUE);
785244008Srmacklem}
786244008Srmacklem
787244008Srmacklemstatic bool_t
788184588Sdfrsvc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg,
789184588Sdfr    struct sockaddr *addr, struct mbuf *m)
790177633Sdfr{
791177633Sdfr	XDR xdrs;
792177633Sdfr	struct mbuf *mrep;
793184588Sdfr	bool_t stat = TRUE;
794177633Sdfr	int error;
795177633Sdfr
796177633Sdfr	/*
797177633Sdfr	 * Leave space for record mark.
798177633Sdfr	 */
799248195Sglebius	mrep = m_gethdr(M_WAITOK, MT_DATA);
800177633Sdfr	mrep->m_data += sizeof(uint32_t);
801177633Sdfr
802184588Sdfr	xdrmbuf_create(&xdrs, mrep, XDR_ENCODE);
803184588Sdfr
804184588Sdfr	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
805184588Sdfr	    msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
806184588Sdfr		if (!xdr_replymsg(&xdrs, msg))
807184588Sdfr			stat = FALSE;
808184588Sdfr		else
809184588Sdfr			xdrmbuf_append(&xdrs, m);
810184588Sdfr	} else {
811184588Sdfr		stat = xdr_replymsg(&xdrs, msg);
812184588Sdfr	}
813184588Sdfr
814184588Sdfr	if (stat) {
815177633Sdfr		m_fixhdr(mrep);
816177633Sdfr
817177633Sdfr		/*
818177633Sdfr		 * Prepend a record marker containing the reply length.
819177633Sdfr		 */
820243882Sglebius		M_PREPEND(mrep, sizeof(uint32_t), M_WAITOK);
821177633Sdfr		*mtod(mrep, uint32_t *) =
822177633Sdfr			htonl(0x80000000 | (mrep->m_pkthdr.len
823177633Sdfr				- sizeof(uint32_t)));
824177633Sdfr		error = sosend(xprt->xp_socket, NULL, NULL, mrep, NULL,
825177633Sdfr		    0, curthread);
826177633Sdfr		if (!error) {
827177633Sdfr			stat = TRUE;
828177633Sdfr		}
829177633Sdfr	} else {
830177633Sdfr		m_freem(mrep);
831177633Sdfr	}
832177633Sdfr
833184588Sdfr	XDR_DESTROY(&xdrs);
834177633Sdfr	xprt->xp_p2 = NULL;
835177633Sdfr
836177633Sdfr	return (stat);
837177633Sdfr}
838177633Sdfr
839177633Sdfrstatic bool_t
840244008Srmacklemsvc_vc_backchannel_reply(SVCXPRT *xprt, struct rpc_msg *msg,
841244008Srmacklem    struct sockaddr *addr, struct mbuf *m)
842244008Srmacklem{
843244008Srmacklem	struct ct_data *ct;
844244008Srmacklem	XDR xdrs;
845244008Srmacklem	struct mbuf *mrep;
846244008Srmacklem	bool_t stat = TRUE;
847244008Srmacklem	int error;
848244008Srmacklem
849244008Srmacklem	/*
850244008Srmacklem	 * Leave space for record mark.
851244008Srmacklem	 */
852248195Sglebius	mrep = m_gethdr(M_WAITOK, MT_DATA);
853244008Srmacklem	mrep->m_data += sizeof(uint32_t);
854244008Srmacklem
855244008Srmacklem	xdrmbuf_create(&xdrs, mrep, XDR_ENCODE);
856244008Srmacklem
857244008Srmacklem	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
858244008Srmacklem	    msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
859244008Srmacklem		if (!xdr_replymsg(&xdrs, msg))
860244008Srmacklem			stat = FALSE;
861244008Srmacklem		else
862244008Srmacklem			xdrmbuf_append(&xdrs, m);
863244008Srmacklem	} else {
864244008Srmacklem		stat = xdr_replymsg(&xdrs, msg);
865244008Srmacklem	}
866244008Srmacklem
867244008Srmacklem	if (stat) {
868244008Srmacklem		m_fixhdr(mrep);
869244008Srmacklem
870244008Srmacklem		/*
871244008Srmacklem		 * Prepend a record marker containing the reply length.
872244008Srmacklem		 */
873244008Srmacklem		M_PREPEND(mrep, sizeof(uint32_t), M_WAITOK);
874244008Srmacklem		*mtod(mrep, uint32_t *) =
875244008Srmacklem			htonl(0x80000000 | (mrep->m_pkthdr.len
876244008Srmacklem				- sizeof(uint32_t)));
877244008Srmacklem		sx_xlock(&xprt->xp_lock);
878244008Srmacklem		ct = (struct ct_data *)xprt->xp_p2;
879244008Srmacklem		if (ct != NULL)
880244008Srmacklem			error = sosend(ct->ct_socket, NULL, NULL, mrep, NULL,
881244008Srmacklem			    0, curthread);
882244008Srmacklem		else
883244008Srmacklem			error = EPIPE;
884244008Srmacklem		sx_xunlock(&xprt->xp_lock);
885244008Srmacklem		if (!error) {
886244008Srmacklem			stat = TRUE;
887244008Srmacklem		}
888244008Srmacklem	} else {
889244008Srmacklem		m_freem(mrep);
890244008Srmacklem	}
891244008Srmacklem
892244008Srmacklem	XDR_DESTROY(&xdrs);
893244008Srmacklem
894244008Srmacklem	return (stat);
895244008Srmacklem}
896244008Srmacklem
897244008Srmacklemstatic bool_t
898177633Sdfrsvc_vc_null()
899177633Sdfr{
900177633Sdfr
901177633Sdfr	return (FALSE);
902177633Sdfr}
903177633Sdfr
904193272Sjhbstatic int
905177633Sdfrsvc_vc_soupcall(struct socket *so, void *arg, int waitflag)
906177633Sdfr{
907177633Sdfr	SVCXPRT *xprt = (SVCXPRT *) arg;
908177633Sdfr
909177633Sdfr	xprt_active(xprt);
910193272Sjhb	return (SU_OK);
911177633Sdfr}
912177633Sdfr
913177633Sdfr#if 0
914177633Sdfr/*
915177633Sdfr * Get the effective UID of the sending process. Used by rpcbind, keyserv
916177633Sdfr * and rpc.yppasswdd on AF_LOCAL.
917177633Sdfr */
918177633Sdfrint
919177633Sdfr__rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) {
920177633Sdfr	int sock, ret;
921177633Sdfr	gid_t egid;
922177633Sdfr	uid_t euid;
923177633Sdfr	struct sockaddr *sa;
924177633Sdfr
925177633Sdfr	sock = transp->xp_fd;
926184588Sdfr	sa = (struct sockaddr *)transp->xp_rtaddr;
927177633Sdfr	if (sa->sa_family == AF_LOCAL) {
928177633Sdfr		ret = getpeereid(sock, &euid, &egid);
929177633Sdfr		if (ret == 0)
930177633Sdfr			*uid = euid;
931177633Sdfr		return (ret);
932177633Sdfr	} else
933177633Sdfr		return (-1);
934177633Sdfr}
935177633Sdfr#endif
936