xref: /freebsd-src/sys/rpc/clnt_vc.c (revision 6a876e97f702c277ea41ad9c90709a4b079c74a3)
1dfdcada3SDoug Rabson /*	$NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $	*/
2dfdcada3SDoug Rabson 
32e322d37SHiroki Sato /*-
451369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
551369649SPedro F. Giffuni  *
62e322d37SHiroki Sato  * Copyright (c) 2009, Sun Microsystems, Inc.
72e322d37SHiroki Sato  * All rights reserved.
8dfdcada3SDoug Rabson  *
92e322d37SHiroki Sato  * Redistribution and use in source and binary forms, with or without
102e322d37SHiroki Sato  * modification, are permitted provided that the following conditions are met:
112e322d37SHiroki Sato  * - Redistributions of source code must retain the above copyright notice,
122e322d37SHiroki Sato  *   this list of conditions and the following disclaimer.
132e322d37SHiroki Sato  * - Redistributions in binary form must reproduce the above copyright notice,
142e322d37SHiroki Sato  *   this list of conditions and the following disclaimer in the documentation
152e322d37SHiroki Sato  *   and/or other materials provided with the distribution.
162e322d37SHiroki Sato  * - Neither the name of Sun Microsystems, Inc. nor the names of its
172e322d37SHiroki Sato  *   contributors may be used to endorse or promote products derived
182e322d37SHiroki Sato  *   from this software without specific prior written permission.
19dfdcada3SDoug Rabson  *
202e322d37SHiroki Sato  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
212e322d37SHiroki Sato  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
222e322d37SHiroki Sato  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
232e322d37SHiroki Sato  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
242e322d37SHiroki Sato  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
252e322d37SHiroki Sato  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
262e322d37SHiroki Sato  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
272e322d37SHiroki Sato  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
282e322d37SHiroki Sato  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
292e322d37SHiroki Sato  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
302e322d37SHiroki Sato  * POSSIBILITY OF SUCH DAMAGE.
31dfdcada3SDoug Rabson  */
32dfdcada3SDoug Rabson 
33dfdcada3SDoug Rabson #include <sys/cdefs.h>
34dfdcada3SDoug Rabson /*
35dfdcada3SDoug Rabson  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
36dfdcada3SDoug Rabson  *
37dfdcada3SDoug Rabson  * Copyright (C) 1984, Sun Microsystems, Inc.
38dfdcada3SDoug Rabson  *
39dfdcada3SDoug Rabson  * TCP based RPC supports 'batched calls'.
40dfdcada3SDoug Rabson  * A sequence of calls may be batched-up in a send buffer.  The rpc call
41dfdcada3SDoug Rabson  * return immediately to the client even though the call was not necessarily
42dfdcada3SDoug Rabson  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
43dfdcada3SDoug Rabson  * the rpc timeout value is zero (see clnt.h, rpc).
44dfdcada3SDoug Rabson  *
45dfdcada3SDoug Rabson  * Clients should NOT casually batch calls that in fact return results; that is,
46dfdcada3SDoug Rabson  * the server side should be aware that a call is batched and not produce any
47dfdcada3SDoug Rabson  * return message.  Batched calls that produce many result messages can
48dfdcada3SDoug Rabson  * deadlock (netlock) the client and the server....
49dfdcada3SDoug Rabson  *
50dfdcada3SDoug Rabson  * Now go hang yourself.
51dfdcada3SDoug Rabson  */
52dfdcada3SDoug Rabson 
53ab0c29afSRick Macklem #include "opt_kern_tls.h"
54ab0c29afSRick Macklem 
55dfdcada3SDoug Rabson #include <sys/param.h>
56dfdcada3SDoug Rabson #include <sys/systm.h>
57dfd174d6SRick Macklem #include <sys/kernel.h>
58ab0c29afSRick Macklem #include <sys/kthread.h>
59ab0c29afSRick Macklem #include <sys/ktls.h>
60dfdcada3SDoug Rabson #include <sys/lock.h>
61dfdcada3SDoug Rabson #include <sys/malloc.h>
62dfdcada3SDoug Rabson #include <sys/mbuf.h>
63dfdcada3SDoug Rabson #include <sys/mutex.h>
64dfdcada3SDoug Rabson #include <sys/pcpu.h>
65dfdcada3SDoug Rabson #include <sys/proc.h>
66a9148abdSDoug Rabson #include <sys/protosw.h>
67dfdcada3SDoug Rabson #include <sys/socket.h>
68dfdcada3SDoug Rabson #include <sys/socketvar.h>
69e2adc47dSRick Macklem #include <sys/sx.h>
70dfdcada3SDoug Rabson #include <sys/syslog.h>
71dfdcada3SDoug Rabson #include <sys/time.h>
72dfdcada3SDoug Rabson #include <sys/uio.h>
730348c661SMarko Zec 
740348c661SMarko Zec #include <net/vnet.h>
750348c661SMarko Zec 
76a9148abdSDoug Rabson #include <netinet/tcp.h>
77dfdcada3SDoug Rabson 
78dfdcada3SDoug Rabson #include <rpc/rpc.h>
79ee31b83aSDoug Rabson #include <rpc/rpc_com.h>
80e2adc47dSRick Macklem #include <rpc/krpc.h>
81ab0c29afSRick Macklem #include <rpc/rpcsec_tls.h>
82dfdcada3SDoug Rabson 
83dfdcada3SDoug Rabson struct cmessage {
84dfdcada3SDoug Rabson         struct cmsghdr cmsg;
85dfdcada3SDoug Rabson         struct cmsgcred cmcred;
86dfdcada3SDoug Rabson };
87dfdcada3SDoug Rabson 
88c675522fSDoug Rabson static enum clnt_stat clnt_vc_call(CLIENT *, struct rpc_callextra *,
89a9148abdSDoug Rabson     rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
90dfdcada3SDoug Rabson static void clnt_vc_geterr(CLIENT *, struct rpc_err *);
91dfdcada3SDoug Rabson static bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *);
92dfdcada3SDoug Rabson static void clnt_vc_abort(CLIENT *);
93dfdcada3SDoug Rabson static bool_t clnt_vc_control(CLIENT *, u_int, void *);
94a9148abdSDoug Rabson static void clnt_vc_close(CLIENT *);
95dfdcada3SDoug Rabson static void clnt_vc_destroy(CLIENT *);
96dfdcada3SDoug Rabson static bool_t time_not_ok(struct timeval *);
9774fb0ba7SJohn Baldwin static int clnt_vc_soupcall(struct socket *so, void *arg, int waitflag);
98ab0c29afSRick Macklem static void clnt_vc_dotlsupcall(void *data);
99dfdcada3SDoug Rabson 
10020d728b5SMark Johnston static const struct clnt_ops clnt_vc_ops = {
101dfdcada3SDoug Rabson 	.cl_call =	clnt_vc_call,
102dfdcada3SDoug Rabson 	.cl_abort =	clnt_vc_abort,
103dfdcada3SDoug Rabson 	.cl_geterr =	clnt_vc_geterr,
104dfdcada3SDoug Rabson 	.cl_freeres =	clnt_vc_freeres,
105a9148abdSDoug Rabson 	.cl_close =	clnt_vc_close,
106dfdcada3SDoug Rabson 	.cl_destroy =	clnt_vc_destroy,
107dfdcada3SDoug Rabson 	.cl_control =	clnt_vc_control
108dfdcada3SDoug Rabson };
109dfdcada3SDoug Rabson 
1103144f812SRick Macklem static void clnt_vc_upcallsdone(struct ct_data *);
1113144f812SRick Macklem 
112dfdcada3SDoug Rabson /*
113dfdcada3SDoug Rabson  * Create a client handle for a connection.
114dfdcada3SDoug Rabson  * Default options are set, which the user can change using clnt_control()'s.
115dfdcada3SDoug Rabson  * The rpc/vc package does buffering similar to stdio, so the client
116dfdcada3SDoug Rabson  * must pick send and receive buffer sizes, 0 => use the default.
117dfdcada3SDoug Rabson  * NB: fd is copied into a private area.
118dfdcada3SDoug Rabson  * NB: The rpch->cl_auth is set null authentication. Caller may wish to
119dfdcada3SDoug Rabson  * set this something more useful.
120dfdcada3SDoug Rabson  *
121dfdcada3SDoug Rabson  * fd should be an open socket
122dfdcada3SDoug Rabson  */
123dfdcada3SDoug Rabson CLIENT *
124dfdcada3SDoug Rabson clnt_vc_create(
125dfdcada3SDoug Rabson 	struct socket *so,		/* open file descriptor */
126dfdcada3SDoug Rabson 	struct sockaddr *raddr,		/* servers address */
127dfdcada3SDoug Rabson 	const rpcprog_t prog,		/* program number */
128dfdcada3SDoug Rabson 	const rpcvers_t vers,		/* version number */
129dfdcada3SDoug Rabson 	size_t sendsz,			/* buffer recv size */
1307b67bd9fSRick Macklem 	size_t recvsz,			/* buffer send size */
1317b67bd9fSRick Macklem 	int intrflag)			/* interruptible */
132dfdcada3SDoug Rabson {
133dfdcada3SDoug Rabson 	CLIENT *cl;			/* client handle */
134dfdcada3SDoug Rabson 	struct ct_data *ct = NULL;	/* client handle */
135dfdcada3SDoug Rabson 	struct timeval now;
136dfdcada3SDoug Rabson 	struct rpc_msg call_msg;
137dfdcada3SDoug Rabson 	static uint32_t disrupt;
138dfdcada3SDoug Rabson 	struct __rpc_sockinfo si;
139dfdcada3SDoug Rabson 	XDR xdrs;
1407b67bd9fSRick Macklem 	int error, interrupted, one = 1, sleep_flag;
141a9148abdSDoug Rabson 	struct sockopt sopt;
142dfdcada3SDoug Rabson 
143dfdcada3SDoug Rabson 	if (disrupt == 0)
144dfdcada3SDoug Rabson 		disrupt = (uint32_t)(long)raddr;
145dfdcada3SDoug Rabson 
146dfdcada3SDoug Rabson 	cl = (CLIENT *)mem_alloc(sizeof (*cl));
147dfdcada3SDoug Rabson 	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
148dfdcada3SDoug Rabson 
149dfdcada3SDoug Rabson 	mtx_init(&ct->ct_lock, "ct->ct_lock", NULL, MTX_DEF);
150c675522fSDoug Rabson 	ct->ct_threads = 0;
151c675522fSDoug Rabson 	ct->ct_closing = FALSE;
152a9148abdSDoug Rabson 	ct->ct_closed = FALSE;
1533144f812SRick Macklem 	ct->ct_upcallrefs = 0;
154ab0c29afSRick Macklem 	ct->ct_rcvstate = RPCRCVSTATE_NORMAL;
155dfdcada3SDoug Rabson 
156f79a8585SGleb Smirnoff 	if ((so->so_state & SS_ISCONNECTED) == 0) {
157dfdcada3SDoug Rabson 		error = soconnect(so, raddr, curthread);
158c675522fSDoug Rabson 		SOCK_LOCK(so);
159c675522fSDoug Rabson 		interrupted = 0;
1607b67bd9fSRick Macklem 		sleep_flag = PSOCK;
1617b67bd9fSRick Macklem 		if (intrflag != 0)
1623b14c753SJohn Baldwin 			sleep_flag |= PCATCH;
163c675522fSDoug Rabson 		while ((so->so_state & SS_ISCONNECTING)
164c675522fSDoug Rabson 		    && so->so_error == 0) {
165c675522fSDoug Rabson 			error = msleep(&so->so_timeo, SOCK_MTX(so),
1667b67bd9fSRick Macklem 			    sleep_flag, "connec", 0);
167dfdcada3SDoug Rabson 			if (error) {
168c675522fSDoug Rabson 				if (error == EINTR || error == ERESTART)
169c675522fSDoug Rabson 					interrupted = 1;
170c675522fSDoug Rabson 				break;
171c675522fSDoug Rabson 			}
172c675522fSDoug Rabson 		}
173c675522fSDoug Rabson 		if (error == 0) {
174c675522fSDoug Rabson 			error = so->so_error;
175c675522fSDoug Rabson 			so->so_error = 0;
176c675522fSDoug Rabson 		}
177c675522fSDoug Rabson 		SOCK_UNLOCK(so);
178c675522fSDoug Rabson 		if (error) {
179c675522fSDoug Rabson 			if (!interrupted)
180c675522fSDoug Rabson 				so->so_state &= ~SS_ISCONNECTING;
181dfdcada3SDoug Rabson 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
182dfdcada3SDoug Rabson 			rpc_createerr.cf_error.re_errno = error;
183dfdcada3SDoug Rabson 			goto err;
184dfdcada3SDoug Rabson 		}
185dfdcada3SDoug Rabson 	}
186dfdcada3SDoug Rabson 
1870348c661SMarko Zec 	if (!__rpc_socket2sockinfo(so, &si)) {
188dfdcada3SDoug Rabson 		goto err;
1890348c661SMarko Zec 	}
190dfdcada3SDoug Rabson 
191a9148abdSDoug Rabson 	if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
192a9148abdSDoug Rabson 		bzero(&sopt, sizeof(sopt));
193a9148abdSDoug Rabson 		sopt.sopt_dir = SOPT_SET;
194a9148abdSDoug Rabson 		sopt.sopt_level = SOL_SOCKET;
195a9148abdSDoug Rabson 		sopt.sopt_name = SO_KEEPALIVE;
196a9148abdSDoug Rabson 		sopt.sopt_val = &one;
197a9148abdSDoug Rabson 		sopt.sopt_valsize = sizeof(one);
198a9148abdSDoug Rabson 		sosetopt(so, &sopt);
199a9148abdSDoug Rabson 	}
200a9148abdSDoug Rabson 
201a9148abdSDoug Rabson 	if (so->so_proto->pr_protocol == IPPROTO_TCP) {
202a9148abdSDoug Rabson 		bzero(&sopt, sizeof(sopt));
203a9148abdSDoug Rabson 		sopt.sopt_dir = SOPT_SET;
204a9148abdSDoug Rabson 		sopt.sopt_level = IPPROTO_TCP;
205a9148abdSDoug Rabson 		sopt.sopt_name = TCP_NODELAY;
206a9148abdSDoug Rabson 		sopt.sopt_val = &one;
207a9148abdSDoug Rabson 		sopt.sopt_valsize = sizeof(one);
208a9148abdSDoug Rabson 		sosetopt(so, &sopt);
209a9148abdSDoug Rabson 	}
210a9148abdSDoug Rabson 
211dfdcada3SDoug Rabson 	ct->ct_closeit = FALSE;
212dfdcada3SDoug Rabson 
213dfdcada3SDoug Rabson 	/*
214dfdcada3SDoug Rabson 	 * Set up private data struct
215dfdcada3SDoug Rabson 	 */
216dfdcada3SDoug Rabson 	ct->ct_socket = so;
217dfdcada3SDoug Rabson 	ct->ct_wait.tv_sec = -1;
218dfdcada3SDoug Rabson 	ct->ct_wait.tv_usec = -1;
219dfdcada3SDoug Rabson 	memcpy(&ct->ct_addr, raddr, raddr->sa_len);
220dfdcada3SDoug Rabson 
221dfdcada3SDoug Rabson 	/*
222dfdcada3SDoug Rabson 	 * Initialize call message
223dfdcada3SDoug Rabson 	 */
224dfdcada3SDoug Rabson 	getmicrotime(&now);
225dfdcada3SDoug Rabson 	ct->ct_xid = ((uint32_t)++disrupt) ^ __RPC_GETXID(&now);
226dfdcada3SDoug Rabson 	call_msg.rm_xid = ct->ct_xid;
227dfdcada3SDoug Rabson 	call_msg.rm_direction = CALL;
228dfdcada3SDoug Rabson 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
229dfdcada3SDoug Rabson 	call_msg.rm_call.cb_prog = (uint32_t)prog;
230dfdcada3SDoug Rabson 	call_msg.rm_call.cb_vers = (uint32_t)vers;
231dfdcada3SDoug Rabson 
232dfdcada3SDoug Rabson 	/*
233dfdcada3SDoug Rabson 	 * pre-serialize the static part of the call msg and stash it away
234dfdcada3SDoug Rabson 	 */
235dfdcada3SDoug Rabson 	xdrmem_create(&xdrs, ct->ct_mcallc, MCALL_MSG_SIZE,
236dfdcada3SDoug Rabson 	    XDR_ENCODE);
2379d04973bSGleb Smirnoff 	if (! xdr_callhdr(&xdrs, &call_msg))
238dfdcada3SDoug Rabson 		goto err;
239dfdcada3SDoug Rabson 	ct->ct_mpos = XDR_GETPOS(&xdrs);
240dfdcada3SDoug Rabson 	XDR_DESTROY(&xdrs);
241dfdcada3SDoug Rabson 	ct->ct_waitchan = "rpcrecv";
242dfdcada3SDoug Rabson 	ct->ct_waitflag = 0;
243dfdcada3SDoug Rabson 
244dfdcada3SDoug Rabson 	/*
245dfdcada3SDoug Rabson 	 * Create a client handle which uses xdrrec for serialization
246dfdcada3SDoug Rabson 	 * and authnone for authentication.
247dfdcada3SDoug Rabson 	 */
248cec077bcSRick Macklem 	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
249cec077bcSRick Macklem 	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
250cec077bcSRick Macklem 	error = soreserve(ct->ct_socket, sendsz, recvsz);
2519d04973bSGleb Smirnoff 	if (error != 0)
252cec077bcSRick Macklem 		goto err;
253c675522fSDoug Rabson 	cl->cl_refs = 1;
254dfdcada3SDoug Rabson 	cl->cl_ops = &clnt_vc_ops;
255dfdcada3SDoug Rabson 	cl->cl_private = ct;
256dfdcada3SDoug Rabson 	cl->cl_auth = authnone_create();
257dfdcada3SDoug Rabson 
258e205fd31SGleb Smirnoff 	SOCK_RECVBUF_LOCK(ct->ct_socket);
25974fb0ba7SJohn Baldwin 	soupcall_set(ct->ct_socket, SO_RCV, clnt_vc_soupcall, ct);
260e205fd31SGleb Smirnoff 	SOCK_RECVBUF_UNLOCK(ct->ct_socket);
261dfdcada3SDoug Rabson 
2624302e8b6SRick Macklem 	ct->ct_raw = NULL;
263dfdcada3SDoug Rabson 	ct->ct_record = NULL;
264dfdcada3SDoug Rabson 	ct->ct_record_resid = 0;
265ab0c29afSRick Macklem 	ct->ct_sslrefno = 0;
266dfdcada3SDoug Rabson 	TAILQ_INIT(&ct->ct_pending);
267dfdcada3SDoug Rabson 	return (cl);
268dfdcada3SDoug Rabson 
269dfdcada3SDoug Rabson err:
270a9ccfd56SDoug Rabson 	mtx_destroy(&ct->ct_lock);
271dfdcada3SDoug Rabson 	mem_free(ct, sizeof (struct ct_data));
272dfdcada3SDoug Rabson 	mem_free(cl, sizeof (CLIENT));
2737d3db235SEnji Cooper 
274dfdcada3SDoug Rabson 	return ((CLIENT *)NULL);
275dfdcada3SDoug Rabson }
276dfdcada3SDoug Rabson 
277dfdcada3SDoug Rabson static enum clnt_stat
278dfdcada3SDoug Rabson clnt_vc_call(
279a9148abdSDoug Rabson 	CLIENT		*cl,		/* client handle */
280a9148abdSDoug Rabson 	struct rpc_callextra *ext,	/* call metadata */
281a9148abdSDoug Rabson 	rpcproc_t	proc,		/* procedure number */
282a9148abdSDoug Rabson 	struct mbuf	*args,		/* pointer to args */
283a9148abdSDoug Rabson 	struct mbuf	**resultsp,	/* pointer to results */
284dfdcada3SDoug Rabson 	struct timeval	utimeout)
285dfdcada3SDoug Rabson {
286dfdcada3SDoug Rabson 	struct ct_data *ct = (struct ct_data *) cl->cl_private;
287c675522fSDoug Rabson 	AUTH *auth;
288a9148abdSDoug Rabson 	struct rpc_err *errp;
289a9148abdSDoug Rabson 	enum clnt_stat stat;
290dfdcada3SDoug Rabson 	XDR xdrs;
291dfdcada3SDoug Rabson 	struct rpc_msg reply_msg;
292dfdcada3SDoug Rabson 	bool_t ok;
293dfdcada3SDoug Rabson 	int nrefreshes = 2;		/* number of times to refresh cred */
294dfdcada3SDoug Rabson 	struct timeval timeout;
295dfdcada3SDoug Rabson 	uint32_t xid;
296a9148abdSDoug Rabson 	struct mbuf *mreq = NULL, *results;
297c675522fSDoug Rabson 	struct ct_request *cr;
298ab0c29afSRick Macklem 	int error, maxextsiz, trycnt;
299ab0c29afSRick Macklem #ifdef KERN_TLS
300ab0c29afSRick Macklem 	u_int maxlen;
301ab0c29afSRick Macklem #endif
302dfdcada3SDoug Rabson 
303c675522fSDoug Rabson 	cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK);
304c675522fSDoug Rabson 
305dfdcada3SDoug Rabson 	mtx_lock(&ct->ct_lock);
306dfdcada3SDoug Rabson 
307a9148abdSDoug Rabson 	if (ct->ct_closing || ct->ct_closed) {
308c675522fSDoug Rabson 		mtx_unlock(&ct->ct_lock);
309c675522fSDoug Rabson 		free(cr, M_RPC);
310c675522fSDoug Rabson 		return (RPC_CANTSEND);
311c675522fSDoug Rabson 	}
312c675522fSDoug Rabson 	ct->ct_threads++;
313c675522fSDoug Rabson 
314a9148abdSDoug Rabson 	if (ext) {
315c675522fSDoug Rabson 		auth = ext->rc_auth;
316a9148abdSDoug Rabson 		errp = &ext->rc_err;
317a9148abdSDoug Rabson 	} else {
318c675522fSDoug Rabson 		auth = cl->cl_auth;
319a9148abdSDoug Rabson 		errp = &ct->ct_error;
320a9148abdSDoug Rabson 	}
321c675522fSDoug Rabson 
322c675522fSDoug Rabson 	cr->cr_mrep = NULL;
323c675522fSDoug Rabson 	cr->cr_error = 0;
324dfdcada3SDoug Rabson 
325dfdcada3SDoug Rabson 	if (ct->ct_wait.tv_usec == -1) {
326dfdcada3SDoug Rabson 		timeout = utimeout;	/* use supplied timeout */
327dfdcada3SDoug Rabson 	} else {
328dfdcada3SDoug Rabson 		timeout = ct->ct_wait;	/* use default timeout */
329dfdcada3SDoug Rabson 	}
330dfdcada3SDoug Rabson 
331dfd174d6SRick Macklem 	/*
332dfd174d6SRick Macklem 	 * After 15sec of looping, allow it to return RPC_CANTSEND, which will
333dfd174d6SRick Macklem 	 * cause the clnt_reconnect layer to create a new TCP connection.
334dfd174d6SRick Macklem 	 */
335dfd174d6SRick Macklem 	trycnt = 15 * hz;
336dfdcada3SDoug Rabson call_again:
337dfdcada3SDoug Rabson 	mtx_assert(&ct->ct_lock, MA_OWNED);
338dfd174d6SRick Macklem 	if (ct->ct_closing || ct->ct_closed) {
339dfd174d6SRick Macklem 		ct->ct_threads--;
340dfd174d6SRick Macklem 		wakeup(ct);
341dfd174d6SRick Macklem 		mtx_unlock(&ct->ct_lock);
342dfd174d6SRick Macklem 		free(cr, M_RPC);
343dfd174d6SRick Macklem 		return (RPC_CANTSEND);
344dfd174d6SRick Macklem 	}
345dfdcada3SDoug Rabson 
346dfdcada3SDoug Rabson 	ct->ct_xid++;
347dfdcada3SDoug Rabson 	xid = ct->ct_xid;
348dfdcada3SDoug Rabson 
349dfdcada3SDoug Rabson 	mtx_unlock(&ct->ct_lock);
350dfdcada3SDoug Rabson 
351dfdcada3SDoug Rabson 	/*
352dfdcada3SDoug Rabson 	 * Leave space to pre-pend the record mark.
353dfdcada3SDoug Rabson 	 */
354bd54830bSGleb Smirnoff 	mreq = m_gethdr(M_WAITOK, MT_DATA);
355dfdcada3SDoug Rabson 	mreq->m_data += sizeof(uint32_t);
356a9148abdSDoug Rabson 	KASSERT(ct->ct_mpos + sizeof(uint32_t) <= MHLEN,
357a9148abdSDoug Rabson 	    ("RPC header too big"));
358a9148abdSDoug Rabson 	bcopy(ct->ct_mcallc, mreq->m_data, ct->ct_mpos);
359a9148abdSDoug Rabson 	mreq->m_len = ct->ct_mpos;
360dfdcada3SDoug Rabson 
361dfdcada3SDoug Rabson 	/*
362dfdcada3SDoug Rabson 	 * The XID is the first thing in the request.
363dfdcada3SDoug Rabson 	 */
364dfdcada3SDoug Rabson 	*mtod(mreq, uint32_t *) = htonl(xid);
365dfdcada3SDoug Rabson 
366dfdcada3SDoug Rabson 	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
367dfdcada3SDoug Rabson 
368a9148abdSDoug Rabson 	errp->re_status = stat = RPC_SUCCESS;
369dfdcada3SDoug Rabson 
370dfdcada3SDoug Rabson 	if ((! XDR_PUTINT32(&xdrs, &proc)) ||
371a9148abdSDoug Rabson 	    (! AUTH_MARSHALL(auth, xid, &xdrs,
372a9148abdSDoug Rabson 		m_copym(args, 0, M_COPYALL, M_WAITOK)))) {
373a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTENCODEARGS;
374c675522fSDoug Rabson 		mtx_lock(&ct->ct_lock);
375c675522fSDoug Rabson 		goto out;
376dfdcada3SDoug Rabson 	}
377a9148abdSDoug Rabson 	mreq->m_pkthdr.len = m_length(mreq, NULL);
378dfdcada3SDoug Rabson 
379dfdcada3SDoug Rabson 	/*
380dfdcada3SDoug Rabson 	 * Prepend a record marker containing the packet length.
381dfdcada3SDoug Rabson 	 */
382eb1b1807SGleb Smirnoff 	M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK);
383dfdcada3SDoug Rabson 	*mtod(mreq, uint32_t *) =
384dfdcada3SDoug Rabson 		htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t)));
385dfdcada3SDoug Rabson 
386c675522fSDoug Rabson 	cr->cr_xid = xid;
387dfdcada3SDoug Rabson 	mtx_lock(&ct->ct_lock);
388f9917533SRick Macklem 	/*
389f9917533SRick Macklem 	 * Check to see if the other end has already started to close down
390f9917533SRick Macklem 	 * the connection. The upcall will have set ct_error.re_status
391f9917533SRick Macklem 	 * to RPC_CANTRECV if this is the case.
392f9917533SRick Macklem 	 * If the other end starts to close down the connection after this
393f9917533SRick Macklem 	 * point, it will be detected later when cr_error is checked,
394f9917533SRick Macklem 	 * since the request is in the ct_pending queue.
395f9917533SRick Macklem 	 */
396f9917533SRick Macklem 	if (ct->ct_error.re_status == RPC_CANTRECV) {
397f9917533SRick Macklem 		if (errp != &ct->ct_error) {
398f9917533SRick Macklem 			errp->re_errno = ct->ct_error.re_errno;
399f9917533SRick Macklem 			errp->re_status = RPC_CANTRECV;
400f9917533SRick Macklem 		}
401f9917533SRick Macklem 		stat = RPC_CANTRECV;
402f9917533SRick Macklem 		goto out;
403f9917533SRick Macklem 	}
404ab0c29afSRick Macklem 
405ab0c29afSRick Macklem 	/* For TLS, wait for an upcall to be done, as required. */
406ab0c29afSRick Macklem 	while ((ct->ct_rcvstate & (RPCRCVSTATE_NORMAL |
407ab0c29afSRick Macklem 	    RPCRCVSTATE_NONAPPDATA)) == 0)
408ab0c29afSRick Macklem 		msleep(&ct->ct_rcvstate, &ct->ct_lock, 0, "rpcrcvst", hz);
409ab0c29afSRick Macklem 
410c675522fSDoug Rabson 	TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link);
411dfdcada3SDoug Rabson 	mtx_unlock(&ct->ct_lock);
412dfdcada3SDoug Rabson 
413ab0c29afSRick Macklem 	if (ct->ct_sslrefno != 0) {
414ab0c29afSRick Macklem 		/*
415ab0c29afSRick Macklem 		 * Copy the mbuf chain to a chain of ext_pgs mbuf(s)
416ab0c29afSRick Macklem 		 * as required by KERN_TLS.
417ab0c29afSRick Macklem 		 */
418ab0c29afSRick Macklem 		maxextsiz = TLS_MAX_MSG_SIZE_V10_2;
419ab0c29afSRick Macklem #ifdef KERN_TLS
420ab0c29afSRick Macklem 		if (rpctls_getinfo(&maxlen, false, false))
421ab0c29afSRick Macklem 			maxextsiz = min(maxextsiz, maxlen);
422ab0c29afSRick Macklem #endif
423ab0c29afSRick Macklem 		mreq = _rpc_copym_into_ext_pgs(mreq, maxextsiz);
424ab0c29afSRick Macklem 	}
425dfdcada3SDoug Rabson 	/*
426dfdcada3SDoug Rabson 	 * sosend consumes mreq.
427dfdcada3SDoug Rabson 	 */
428dfdcada3SDoug Rabson 	error = sosend(ct->ct_socket, NULL, NULL, mreq, NULL, 0, curthread);
429dfdcada3SDoug Rabson 	mreq = NULL;
430dfd174d6SRick Macklem 	if (error == EMSGSIZE || (error == ERESTART &&
431dfd174d6SRick Macklem 	    (ct->ct_waitflag & PCATCH) == 0 && trycnt-- > 0)) {
432e205fd31SGleb Smirnoff 		SOCK_SENDBUF_LOCK(ct->ct_socket);
43343283184SGleb Smirnoff 		sbwait(ct->ct_socket, SO_SND);
434e205fd31SGleb Smirnoff 		SOCK_SENDBUF_UNLOCK(ct->ct_socket);
435a9148abdSDoug Rabson 		AUTH_VALIDATE(auth, xid, NULL, NULL);
436a9148abdSDoug Rabson 		mtx_lock(&ct->ct_lock);
437a9148abdSDoug Rabson 		TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
438dfd174d6SRick Macklem 		/* Sleep for 1 clock tick before trying the sosend() again. */
43982512c17SRick Macklem 		mtx_unlock(&ct->ct_lock);
44082512c17SRick Macklem 		pause("rpclpsnd", 1);
44182512c17SRick Macklem 		mtx_lock(&ct->ct_lock);
442a9148abdSDoug Rabson 		goto call_again;
443a9148abdSDoug Rabson 	}
444dfdcada3SDoug Rabson 
445a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL;
446a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf;
447a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_verf.oa_length = 0;
448a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_results.where = NULL;
449a9148abdSDoug Rabson 	reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
450dfdcada3SDoug Rabson 
451dfdcada3SDoug Rabson 	mtx_lock(&ct->ct_lock);
452dfdcada3SDoug Rabson 	if (error) {
453c675522fSDoug Rabson 		TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
454a9148abdSDoug Rabson 		errp->re_errno = error;
455a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTSEND;
456dfdcada3SDoug Rabson 		goto out;
457dfdcada3SDoug Rabson 	}
458dfdcada3SDoug Rabson 
459dfdcada3SDoug Rabson 	/*
460dfdcada3SDoug Rabson 	 * Check to see if we got an upcall while waiting for the
461dfdcada3SDoug Rabson 	 * lock. In both these cases, the request has been removed
462dfdcada3SDoug Rabson 	 * from ct->ct_pending.
463dfdcada3SDoug Rabson 	 */
464c675522fSDoug Rabson 	if (cr->cr_error) {
465c675522fSDoug Rabson 		TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
466a9148abdSDoug Rabson 		errp->re_errno = cr->cr_error;
467a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTRECV;
468dfdcada3SDoug Rabson 		goto out;
469dfdcada3SDoug Rabson 	}
470c675522fSDoug Rabson 	if (cr->cr_mrep) {
471c675522fSDoug Rabson 		TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
472dfdcada3SDoug Rabson 		goto got_reply;
473dfdcada3SDoug Rabson 	}
474dfdcada3SDoug Rabson 
475dfdcada3SDoug Rabson 	/*
476dfdcada3SDoug Rabson 	 * Hack to provide rpc-based message passing
477dfdcada3SDoug Rabson 	 */
478dfdcada3SDoug Rabson 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
479c675522fSDoug Rabson 		TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
480a9148abdSDoug Rabson 		errp->re_status = stat = RPC_TIMEDOUT;
481dfdcada3SDoug Rabson 		goto out;
482dfdcada3SDoug Rabson 	}
483dfdcada3SDoug Rabson 
484c675522fSDoug Rabson 	error = msleep(cr, &ct->ct_lock, ct->ct_waitflag, ct->ct_waitchan,
485dfdcada3SDoug Rabson 	    tvtohz(&timeout));
486dfdcada3SDoug Rabson 
487c675522fSDoug Rabson 	TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
488c675522fSDoug Rabson 
489dfdcada3SDoug Rabson 	if (error) {
490dfdcada3SDoug Rabson 		/*
491dfdcada3SDoug Rabson 		 * The sleep returned an error so our request is still
492dfdcada3SDoug Rabson 		 * on the list. Turn the error code into an
493dfdcada3SDoug Rabson 		 * appropriate client status.
494dfdcada3SDoug Rabson 		 */
495a9148abdSDoug Rabson 		errp->re_errno = error;
496dfdcada3SDoug Rabson 		switch (error) {
497dfdcada3SDoug Rabson 		case EINTR:
498a9148abdSDoug Rabson 			stat = RPC_INTR;
499dfdcada3SDoug Rabson 			break;
500dfdcada3SDoug Rabson 		case EWOULDBLOCK:
501a9148abdSDoug Rabson 			stat = RPC_TIMEDOUT;
502dfdcada3SDoug Rabson 			break;
503dfdcada3SDoug Rabson 		default:
504a9148abdSDoug Rabson 			stat = RPC_CANTRECV;
505dfdcada3SDoug Rabson 		}
506a9148abdSDoug Rabson 		errp->re_status = stat;
507dfdcada3SDoug Rabson 		goto out;
508dfdcada3SDoug Rabson 	} else {
509dfdcada3SDoug Rabson 		/*
510dfdcada3SDoug Rabson 		 * We were woken up by the upcall.  If the
511dfdcada3SDoug Rabson 		 * upcall had a receive error, report that,
512dfdcada3SDoug Rabson 		 * otherwise we have a reply.
513dfdcada3SDoug Rabson 		 */
514c675522fSDoug Rabson 		if (cr->cr_error) {
515a9148abdSDoug Rabson 			errp->re_errno = cr->cr_error;
516a9148abdSDoug Rabson 			errp->re_status = stat = RPC_CANTRECV;
517dfdcada3SDoug Rabson 			goto out;
518dfdcada3SDoug Rabson 		}
519dfdcada3SDoug Rabson 	}
520dfdcada3SDoug Rabson 
521dfdcada3SDoug Rabson got_reply:
522dfdcada3SDoug Rabson 	/*
523dfdcada3SDoug Rabson 	 * Now decode and validate the response. We need to drop the
524dfdcada3SDoug Rabson 	 * lock since xdr_replymsg may end up sleeping in malloc.
525dfdcada3SDoug Rabson 	 */
526dfdcada3SDoug Rabson 	mtx_unlock(&ct->ct_lock);
527dfdcada3SDoug Rabson 
528a9148abdSDoug Rabson 	if (ext && ext->rc_feedback)
529a9148abdSDoug Rabson 		ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg);
530a9148abdSDoug Rabson 
531c675522fSDoug Rabson 	xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE);
532dfdcada3SDoug Rabson 	ok = xdr_replymsg(&xdrs, &reply_msg);
533c675522fSDoug Rabson 	cr->cr_mrep = NULL;
534dfdcada3SDoug Rabson 
535dfdcada3SDoug Rabson 	if (ok) {
536dfdcada3SDoug Rabson 		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
537dfdcada3SDoug Rabson 		    (reply_msg.acpted_rply.ar_stat == SUCCESS))
538a9148abdSDoug Rabson 			errp->re_status = stat = RPC_SUCCESS;
539dfdcada3SDoug Rabson 		else
540a9148abdSDoug Rabson 			stat = _seterr_reply(&reply_msg, errp);
541dfdcada3SDoug Rabson 
542a9148abdSDoug Rabson 		if (stat == RPC_SUCCESS) {
543a9148abdSDoug Rabson 			results = xdrmbuf_getall(&xdrs);
544a9148abdSDoug Rabson 			if (!AUTH_VALIDATE(auth, xid,
545a9148abdSDoug Rabson 				&reply_msg.acpted_rply.ar_verf,
546a9148abdSDoug Rabson 				&results)) {
547a9148abdSDoug Rabson 				errp->re_status = stat = RPC_AUTHERROR;
548a9148abdSDoug Rabson 				errp->re_why = AUTH_INVALIDRESP;
549a9148abdSDoug Rabson 			} else {
550a9148abdSDoug Rabson 				KASSERT(results,
551a9148abdSDoug Rabson 				    ("auth validated but no result"));
552a9148abdSDoug Rabson 				*resultsp = results;
553dfdcada3SDoug Rabson 			}
554dfdcada3SDoug Rabson 		}		/* end successful completion */
555dfdcada3SDoug Rabson 		/*
5566244c6e7SPedro F. Giffuni 		 * If unsuccessful AND error is an authentication error
557dfdcada3SDoug Rabson 		 * then refresh credentials and try again, else break
558dfdcada3SDoug Rabson 		 */
559a9148abdSDoug Rabson 		else if (stat == RPC_AUTHERROR)
560dfdcada3SDoug Rabson 			/* maybe our credentials need to be refreshed ... */
561dfdcada3SDoug Rabson 			if (nrefreshes > 0 &&
562a9148abdSDoug Rabson 			    AUTH_REFRESH(auth, &reply_msg)) {
563dfdcada3SDoug Rabson 				nrefreshes--;
564a9148abdSDoug Rabson 				XDR_DESTROY(&xdrs);
565a9148abdSDoug Rabson 				mtx_lock(&ct->ct_lock);
566dfdcada3SDoug Rabson 				goto call_again;
567dfdcada3SDoug Rabson 			}
568dfdcada3SDoug Rabson 		/* end of unsuccessful completion */
569dfdcada3SDoug Rabson 	}	/* end of valid reply message */
570dfdcada3SDoug Rabson 	else {
571a9148abdSDoug Rabson 		errp->re_status = stat = RPC_CANTDECODERES;
572dfdcada3SDoug Rabson 	}
573a9148abdSDoug Rabson 	XDR_DESTROY(&xdrs);
574a9148abdSDoug Rabson 	mtx_lock(&ct->ct_lock);
575dfdcada3SDoug Rabson out:
576dfdcada3SDoug Rabson 	mtx_assert(&ct->ct_lock, MA_OWNED);
577dfdcada3SDoug Rabson 
578a9148abdSDoug Rabson 	KASSERT(stat != RPC_SUCCESS || *resultsp,
579a9148abdSDoug Rabson 	    ("RPC_SUCCESS without reply"));
580a9148abdSDoug Rabson 
581dfdcada3SDoug Rabson 	if (mreq)
582dfdcada3SDoug Rabson 		m_freem(mreq);
583c675522fSDoug Rabson 	if (cr->cr_mrep)
584c675522fSDoug Rabson 		m_freem(cr->cr_mrep);
585c675522fSDoug Rabson 
586c675522fSDoug Rabson 	ct->ct_threads--;
587c675522fSDoug Rabson 	if (ct->ct_closing)
588c675522fSDoug Rabson 		wakeup(ct);
589dfdcada3SDoug Rabson 
590dfdcada3SDoug Rabson 	mtx_unlock(&ct->ct_lock);
591c675522fSDoug Rabson 
592a9148abdSDoug Rabson 	if (auth && stat != RPC_SUCCESS)
593a9148abdSDoug Rabson 		AUTH_VALIDATE(auth, xid, NULL, NULL);
594a9148abdSDoug Rabson 
595c675522fSDoug Rabson 	free(cr, M_RPC);
596c675522fSDoug Rabson 
597a9148abdSDoug Rabson 	return (stat);
598dfdcada3SDoug Rabson }
599dfdcada3SDoug Rabson 
600dfdcada3SDoug Rabson static void
601dfdcada3SDoug Rabson clnt_vc_geterr(CLIENT *cl, struct rpc_err *errp)
602dfdcada3SDoug Rabson {
603dfdcada3SDoug Rabson 	struct ct_data *ct = (struct ct_data *) cl->cl_private;
604dfdcada3SDoug Rabson 
605dfdcada3SDoug Rabson 	*errp = ct->ct_error;
606dfdcada3SDoug Rabson }
607dfdcada3SDoug Rabson 
608dfdcada3SDoug Rabson static bool_t
609dfdcada3SDoug Rabson clnt_vc_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
610dfdcada3SDoug Rabson {
611dfdcada3SDoug Rabson 	XDR xdrs;
612dfdcada3SDoug Rabson 	bool_t dummy;
613dfdcada3SDoug Rabson 
614dfdcada3SDoug Rabson 	xdrs.x_op = XDR_FREE;
615dfdcada3SDoug Rabson 	dummy = (*xdr_res)(&xdrs, res_ptr);
616dfdcada3SDoug Rabson 
617dfdcada3SDoug Rabson 	return (dummy);
618dfdcada3SDoug Rabson }
619dfdcada3SDoug Rabson 
620dfdcada3SDoug Rabson /*ARGSUSED*/
621dfdcada3SDoug Rabson static void
622dfdcada3SDoug Rabson clnt_vc_abort(CLIENT *cl)
623dfdcada3SDoug Rabson {
624dfdcada3SDoug Rabson }
625dfdcada3SDoug Rabson 
626dfdcada3SDoug Rabson static bool_t
627dfdcada3SDoug Rabson clnt_vc_control(CLIENT *cl, u_int request, void *info)
628dfdcada3SDoug Rabson {
629dfdcada3SDoug Rabson 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
630dfdcada3SDoug Rabson 	void *infop = info;
631e2adc47dSRick Macklem 	SVCXPRT *xprt;
632ab0c29afSRick Macklem 	uint64_t *p;
633ab0c29afSRick Macklem 	int error;
634ab0c29afSRick Macklem 	static u_int thrdnum = 0;
635dfdcada3SDoug Rabson 
636dfdcada3SDoug Rabson 	mtx_lock(&ct->ct_lock);
637dfdcada3SDoug Rabson 
638dfdcada3SDoug Rabson 	switch (request) {
639dfdcada3SDoug Rabson 	case CLSET_FD_CLOSE:
640dfdcada3SDoug Rabson 		ct->ct_closeit = TRUE;
641dfdcada3SDoug Rabson 		mtx_unlock(&ct->ct_lock);
642dfdcada3SDoug Rabson 		return (TRUE);
643dfdcada3SDoug Rabson 	case CLSET_FD_NCLOSE:
644dfdcada3SDoug Rabson 		ct->ct_closeit = FALSE;
645dfdcada3SDoug Rabson 		mtx_unlock(&ct->ct_lock);
646dfdcada3SDoug Rabson 		return (TRUE);
647dfdcada3SDoug Rabson 	default:
648dfdcada3SDoug Rabson 		break;
649dfdcada3SDoug Rabson 	}
650dfdcada3SDoug Rabson 
651dfdcada3SDoug Rabson 	/* for other requests which use info */
652dfdcada3SDoug Rabson 	if (info == NULL) {
653dfdcada3SDoug Rabson 		mtx_unlock(&ct->ct_lock);
654dfdcada3SDoug Rabson 		return (FALSE);
655dfdcada3SDoug Rabson 	}
656dfdcada3SDoug Rabson 	switch (request) {
657dfdcada3SDoug Rabson 	case CLSET_TIMEOUT:
658dfdcada3SDoug Rabson 		if (time_not_ok((struct timeval *)info)) {
659dfdcada3SDoug Rabson 			mtx_unlock(&ct->ct_lock);
660dfdcada3SDoug Rabson 			return (FALSE);
661dfdcada3SDoug Rabson 		}
662dfdcada3SDoug Rabson 		ct->ct_wait = *(struct timeval *)infop;
663dfdcada3SDoug Rabson 		break;
664dfdcada3SDoug Rabson 	case CLGET_TIMEOUT:
665dfdcada3SDoug Rabson 		*(struct timeval *)infop = ct->ct_wait;
666dfdcada3SDoug Rabson 		break;
667dfdcada3SDoug Rabson 	case CLGET_SERVER_ADDR:
668dfdcada3SDoug Rabson 		(void) memcpy(info, &ct->ct_addr, (size_t)ct->ct_addr.ss_len);
669dfdcada3SDoug Rabson 		break;
670dfdcada3SDoug Rabson 	case CLGET_SVC_ADDR:
671dfdcada3SDoug Rabson 		/*
672dfdcada3SDoug Rabson 		 * Slightly different semantics to userland - we use
673dfdcada3SDoug Rabson 		 * sockaddr instead of netbuf.
674dfdcada3SDoug Rabson 		 */
675dfdcada3SDoug Rabson 		memcpy(info, &ct->ct_addr, ct->ct_addr.ss_len);
676dfdcada3SDoug Rabson 		break;
677dfdcada3SDoug Rabson 	case CLSET_SVC_ADDR:		/* set to new address */
678dfdcada3SDoug Rabson 		mtx_unlock(&ct->ct_lock);
679dfdcada3SDoug Rabson 		return (FALSE);
680dfdcada3SDoug Rabson 	case CLGET_XID:
681dfdcada3SDoug Rabson 		*(uint32_t *)info = ct->ct_xid;
682dfdcada3SDoug Rabson 		break;
683dfdcada3SDoug Rabson 	case CLSET_XID:
684dfdcada3SDoug Rabson 		/* This will set the xid of the NEXT call */
685dfdcada3SDoug Rabson 		/* decrement by 1 as clnt_vc_call() increments once */
686dfdcada3SDoug Rabson 		ct->ct_xid = *(uint32_t *)info - 1;
687dfdcada3SDoug Rabson 		break;
688dfdcada3SDoug Rabson 	case CLGET_VERS:
689dfdcada3SDoug Rabson 		/*
690dfdcada3SDoug Rabson 		 * This RELIES on the information that, in the call body,
691dfdcada3SDoug Rabson 		 * the version number field is the fifth field from the
6926244c6e7SPedro F. Giffuni 		 * beginning of the RPC header. MUST be changed if the
693dfdcada3SDoug Rabson 		 * call_struct is changed
694dfdcada3SDoug Rabson 		 */
695dfdcada3SDoug Rabson 		*(uint32_t *)info =
696dfdcada3SDoug Rabson 		    ntohl(*(uint32_t *)(void *)(ct->ct_mcallc +
697dfdcada3SDoug Rabson 		    4 * BYTES_PER_XDR_UNIT));
698dfdcada3SDoug Rabson 		break;
699dfdcada3SDoug Rabson 
700dfdcada3SDoug Rabson 	case CLSET_VERS:
701dfdcada3SDoug Rabson 		*(uint32_t *)(void *)(ct->ct_mcallc +
702dfdcada3SDoug Rabson 		    4 * BYTES_PER_XDR_UNIT) =
703dfdcada3SDoug Rabson 		    htonl(*(uint32_t *)info);
704dfdcada3SDoug Rabson 		break;
705dfdcada3SDoug Rabson 
706dfdcada3SDoug Rabson 	case CLGET_PROG:
707dfdcada3SDoug Rabson 		/*
708dfdcada3SDoug Rabson 		 * This RELIES on the information that, in the call body,
709dfdcada3SDoug Rabson 		 * the program number field is the fourth field from the
7106244c6e7SPedro F. Giffuni 		 * beginning of the RPC header. MUST be changed if the
711dfdcada3SDoug Rabson 		 * call_struct is changed
712dfdcada3SDoug Rabson 		 */
713dfdcada3SDoug Rabson 		*(uint32_t *)info =
714dfdcada3SDoug Rabson 		    ntohl(*(uint32_t *)(void *)(ct->ct_mcallc +
715dfdcada3SDoug Rabson 		    3 * BYTES_PER_XDR_UNIT));
716dfdcada3SDoug Rabson 		break;
717dfdcada3SDoug Rabson 
718dfdcada3SDoug Rabson 	case CLSET_PROG:
719dfdcada3SDoug Rabson 		*(uint32_t *)(void *)(ct->ct_mcallc +
720dfdcada3SDoug Rabson 		    3 * BYTES_PER_XDR_UNIT) =
721dfdcada3SDoug Rabson 		    htonl(*(uint32_t *)info);
722dfdcada3SDoug Rabson 		break;
723dfdcada3SDoug Rabson 
724dfdcada3SDoug Rabson 	case CLSET_WAITCHAN:
725a9148abdSDoug Rabson 		ct->ct_waitchan = (const char *)info;
726dfdcada3SDoug Rabson 		break;
727dfdcada3SDoug Rabson 
728dfdcada3SDoug Rabson 	case CLGET_WAITCHAN:
729dfdcada3SDoug Rabson 		*(const char **) info = ct->ct_waitchan;
730dfdcada3SDoug Rabson 		break;
731dfdcada3SDoug Rabson 
732dfdcada3SDoug Rabson 	case CLSET_INTERRUPTIBLE:
733dfdcada3SDoug Rabson 		if (*(int *) info)
7343b14c753SJohn Baldwin 			ct->ct_waitflag = PCATCH;
735dfdcada3SDoug Rabson 		else
736dfdcada3SDoug Rabson 			ct->ct_waitflag = 0;
737dfdcada3SDoug Rabson 		break;
738dfdcada3SDoug Rabson 
739dfdcada3SDoug Rabson 	case CLGET_INTERRUPTIBLE:
740dfdcada3SDoug Rabson 		if (ct->ct_waitflag)
741dfdcada3SDoug Rabson 			*(int *) info = TRUE;
742dfdcada3SDoug Rabson 		else
743dfdcada3SDoug Rabson 			*(int *) info = FALSE;
744dfdcada3SDoug Rabson 		break;
745dfdcada3SDoug Rabson 
746e2adc47dSRick Macklem 	case CLSET_BACKCHANNEL:
747e2adc47dSRick Macklem 		xprt = (SVCXPRT *)info;
748e2adc47dSRick Macklem 		if (ct->ct_backchannelxprt == NULL) {
7494ba444deSRick Macklem 			SVC_ACQUIRE(xprt);
750e2adc47dSRick Macklem 			xprt->xp_p2 = ct;
751ab0c29afSRick Macklem 			if (ct->ct_sslrefno != 0)
752ab0c29afSRick Macklem 				xprt->xp_tls = RPCTLS_FLAGS_HANDSHAKE;
753e2adc47dSRick Macklem 			ct->ct_backchannelxprt = xprt;
754e2adc47dSRick Macklem 		}
755e2adc47dSRick Macklem 		break;
756e2adc47dSRick Macklem 
757ab0c29afSRick Macklem 	case CLSET_TLS:
758ab0c29afSRick Macklem 		p = (uint64_t *)info;
759ab0c29afSRick Macklem 		ct->ct_sslsec = *p++;
760ab0c29afSRick Macklem 		ct->ct_sslusec = *p++;
761ab0c29afSRick Macklem 		ct->ct_sslrefno = *p;
762ab0c29afSRick Macklem 		if (ct->ct_sslrefno != RPCTLS_REFNO_HANDSHAKE) {
7634ba444deSRick Macklem 			/* cl ref cnt is released by clnt_vc_dotlsupcall(). */
7644ba444deSRick Macklem 			CLNT_ACQUIRE(cl);
765ab0c29afSRick Macklem 			mtx_unlock(&ct->ct_lock);
766ab0c29afSRick Macklem 			/* Start the kthread that handles upcalls. */
7674ba444deSRick Macklem 			error = kthread_add(clnt_vc_dotlsupcall, cl,
768ab0c29afSRick Macklem 			    NULL, NULL, 0, 0, "krpctls%u", thrdnum++);
769ab0c29afSRick Macklem 			if (error != 0)
770ab0c29afSRick Macklem 				panic("Can't add KRPC thread error %d", error);
771ab0c29afSRick Macklem 		} else
772ab0c29afSRick Macklem 			mtx_unlock(&ct->ct_lock);
773ab0c29afSRick Macklem 		return (TRUE);
774ab0c29afSRick Macklem 
775ab0c29afSRick Macklem 	case CLSET_BLOCKRCV:
776ab0c29afSRick Macklem 		if (*(int *) info) {
777ab0c29afSRick Macklem 			ct->ct_rcvstate &= ~RPCRCVSTATE_NORMAL;
778ab0c29afSRick Macklem 			ct->ct_rcvstate |= RPCRCVSTATE_TLSHANDSHAKE;
779ab0c29afSRick Macklem 		} else {
780ab0c29afSRick Macklem 			ct->ct_rcvstate &= ~RPCRCVSTATE_TLSHANDSHAKE;
781ab0c29afSRick Macklem 			ct->ct_rcvstate |= RPCRCVSTATE_NORMAL;
782ab0c29afSRick Macklem 		}
783ab0c29afSRick Macklem 		break;
784ab0c29afSRick Macklem 
785dfdcada3SDoug Rabson 	default:
786dfdcada3SDoug Rabson 		mtx_unlock(&ct->ct_lock);
787dfdcada3SDoug Rabson 		return (FALSE);
788dfdcada3SDoug Rabson 	}
789dfdcada3SDoug Rabson 
790dfdcada3SDoug Rabson 	mtx_unlock(&ct->ct_lock);
791dfdcada3SDoug Rabson 	return (TRUE);
792dfdcada3SDoug Rabson }
793dfdcada3SDoug Rabson 
794dfdcada3SDoug Rabson static void
795a9148abdSDoug Rabson clnt_vc_close(CLIENT *cl)
796dfdcada3SDoug Rabson {
797dfdcada3SDoug Rabson 	struct ct_data *ct = (struct ct_data *) cl->cl_private;
798c675522fSDoug Rabson 	struct ct_request *cr;
799dfdcada3SDoug Rabson 
800dfdcada3SDoug Rabson 	mtx_lock(&ct->ct_lock);
801dfdcada3SDoug Rabson 
802a9148abdSDoug Rabson 	if (ct->ct_closed) {
803a9148abdSDoug Rabson 		mtx_unlock(&ct->ct_lock);
804a9148abdSDoug Rabson 		return;
805a9148abdSDoug Rabson 	}
806a9148abdSDoug Rabson 
807a9148abdSDoug Rabson 	if (ct->ct_closing) {
808a9148abdSDoug Rabson 		while (ct->ct_closing)
809a9148abdSDoug Rabson 			msleep(ct, &ct->ct_lock, 0, "rpcclose", 0);
810a9148abdSDoug Rabson 		KASSERT(ct->ct_closed, ("client should be closed"));
811a9148abdSDoug Rabson 		mtx_unlock(&ct->ct_lock);
812a9148abdSDoug Rabson 		return;
813a9148abdSDoug Rabson 	}
814a9148abdSDoug Rabson 
815dfdcada3SDoug Rabson 	if (ct->ct_socket) {
81674fb0ba7SJohn Baldwin 		ct->ct_closing = TRUE;
81774fb0ba7SJohn Baldwin 		mtx_unlock(&ct->ct_lock);
81874fb0ba7SJohn Baldwin 
819e205fd31SGleb Smirnoff 		SOCK_RECVBUF_LOCK(ct->ct_socket);
820ab0c29afSRick Macklem 		if (ct->ct_socket->so_rcv.sb_upcall != NULL) {
82174fb0ba7SJohn Baldwin 			soupcall_clear(ct->ct_socket, SO_RCV);
8223144f812SRick Macklem 			clnt_vc_upcallsdone(ct);
823ab0c29afSRick Macklem 		}
824e205fd31SGleb Smirnoff 		SOCK_RECVBUF_UNLOCK(ct->ct_socket);
825dfdcada3SDoug Rabson 
826c675522fSDoug Rabson 		/*
827c675522fSDoug Rabson 		 * Abort any pending requests and wait until everyone
828c675522fSDoug Rabson 		 * has finished with clnt_vc_call.
829c675522fSDoug Rabson 		 */
83074fb0ba7SJohn Baldwin 		mtx_lock(&ct->ct_lock);
831c675522fSDoug Rabson 		TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) {
832c675522fSDoug Rabson 			cr->cr_xid = 0;
833c675522fSDoug Rabson 			cr->cr_error = ESHUTDOWN;
834c675522fSDoug Rabson 			wakeup(cr);
835c675522fSDoug Rabson 		}
836c675522fSDoug Rabson 
837c675522fSDoug Rabson 		while (ct->ct_threads)
838c675522fSDoug Rabson 			msleep(ct, &ct->ct_lock, 0, "rpcclose", 0);
839a9148abdSDoug Rabson 	}
840dfdcada3SDoug Rabson 
841a9148abdSDoug Rabson 	ct->ct_closing = FALSE;
842a9148abdSDoug Rabson 	ct->ct_closed = TRUE;
843ab0c29afSRick Macklem 	wakeup(&ct->ct_sslrefno);
844a9148abdSDoug Rabson 	mtx_unlock(&ct->ct_lock);
845a9148abdSDoug Rabson 	wakeup(ct);
846a9148abdSDoug Rabson }
847a9148abdSDoug Rabson 
848a9148abdSDoug Rabson static void
849a9148abdSDoug Rabson clnt_vc_destroy(CLIENT *cl)
850a9148abdSDoug Rabson {
851a9148abdSDoug Rabson 	struct ct_data *ct = (struct ct_data *) cl->cl_private;
852d9f9a73aSGleb Smirnoff 	struct socket *so;
853e2adc47dSRick Macklem 	SVCXPRT *xprt;
854ab0c29afSRick Macklem 	uint32_t reterr;
855a9148abdSDoug Rabson 
856a9148abdSDoug Rabson 	clnt_vc_close(cl);
857a9148abdSDoug Rabson 
858a9148abdSDoug Rabson 	mtx_lock(&ct->ct_lock);
859e2adc47dSRick Macklem 	xprt = ct->ct_backchannelxprt;
860e2adc47dSRick Macklem 	ct->ct_backchannelxprt = NULL;
861e2adc47dSRick Macklem 	if (xprt != NULL) {
862e2adc47dSRick Macklem 		mtx_unlock(&ct->ct_lock);	/* To avoid a LOR. */
863e2adc47dSRick Macklem 		sx_xlock(&xprt->xp_lock);
864e2adc47dSRick Macklem 		mtx_lock(&ct->ct_lock);
865e2adc47dSRick Macklem 		xprt->xp_p2 = NULL;
86634f1fddbSRick Macklem 		sx_xunlock(&xprt->xp_lock);
8674ba444deSRick Macklem 		SVC_RELEASE(xprt);
868e2adc47dSRick Macklem 	}
869a9148abdSDoug Rabson 
870ab0c29afSRick Macklem 	/* Wait for the upcall kthread to terminate. */
871ab0c29afSRick Macklem 	while ((ct->ct_rcvstate & RPCRCVSTATE_UPCALLTHREAD) != 0)
872ab0c29afSRick Macklem 		msleep(&ct->ct_sslrefno, &ct->ct_lock, 0,
873ab0c29afSRick Macklem 		    "clntvccl", hz);
874dfdcada3SDoug Rabson 	mtx_unlock(&ct->ct_lock);
875dfdcada3SDoug Rabson 	mtx_destroy(&ct->ct_lock);
876d9f9a73aSGleb Smirnoff 
877d9f9a73aSGleb Smirnoff 	so = ct->ct_closeit ? ct->ct_socket : NULL;
878dfdcada3SDoug Rabson 	if (so) {
879ab0c29afSRick Macklem 		if (ct->ct_sslrefno != 0) {
880ab0c29afSRick Macklem 			/*
881ab0c29afSRick Macklem 			 * If the TLS handshake is in progress, the upcall
882ab0c29afSRick Macklem 			 * will fail, but the socket should be closed by the
883ab0c29afSRick Macklem 			 * daemon, since the connect upcall has just failed.
884ab0c29afSRick Macklem 			 */
885ab0c29afSRick Macklem 			if (ct->ct_sslrefno != RPCTLS_REFNO_HANDSHAKE) {
886ab0c29afSRick Macklem 				/*
887ab0c29afSRick Macklem 				 * If the upcall fails, the socket has
888ab0c29afSRick Macklem 				 * probably been closed via the rpctlscd
889ab0c29afSRick Macklem 				 * daemon having crashed or been
890ab0c29afSRick Macklem 				 * restarted, so ignore return stat.
891ab0c29afSRick Macklem 				 */
89277bc5890SWarner Losh 				rpctls_cl_disconnect(ct->ct_sslsec,
893ab0c29afSRick Macklem 				    ct->ct_sslusec, ct->ct_sslrefno,
894ab0c29afSRick Macklem 				    &reterr);
895ab0c29afSRick Macklem 			}
896ab0c29afSRick Macklem 			/* Must sorele() to get rid of reference. */
897ab0c29afSRick Macklem 			CURVNET_SET(so->so_vnet);
898ab0c29afSRick Macklem 			sorele(so);
899ab0c29afSRick Macklem 			CURVNET_RESTORE();
900ab0c29afSRick Macklem 		} else {
901dfdcada3SDoug Rabson 			soshutdown(so, SHUT_WR);
902dfdcada3SDoug Rabson 			soclose(so);
903dfdcada3SDoug Rabson 		}
904ab0c29afSRick Macklem 	}
9054302e8b6SRick Macklem 	m_freem(ct->ct_record);
9064302e8b6SRick Macklem 	m_freem(ct->ct_raw);
907dfdcada3SDoug Rabson 	mem_free(ct, sizeof(struct ct_data));
9080c2222baSPedro F. Giffuni 	if (cl->cl_netid && cl->cl_netid[0])
9090c2222baSPedro F. Giffuni 		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
9100c2222baSPedro F. Giffuni 	if (cl->cl_tp && cl->cl_tp[0])
9110c2222baSPedro F. Giffuni 		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
912dfdcada3SDoug Rabson 	mem_free(cl, sizeof(CLIENT));
913dfdcada3SDoug Rabson }
914dfdcada3SDoug Rabson 
915dfdcada3SDoug Rabson /*
916dfdcada3SDoug Rabson  * Make sure that the time is not garbage.   -1 value is disallowed.
917dfdcada3SDoug Rabson  * Note this is different from time_not_ok in clnt_dg.c
918dfdcada3SDoug Rabson  */
919dfdcada3SDoug Rabson static bool_t
920dfdcada3SDoug Rabson time_not_ok(struct timeval *t)
921dfdcada3SDoug Rabson {
922dfdcada3SDoug Rabson 	return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
923dfdcada3SDoug Rabson 		t->tv_usec <= -1 || t->tv_usec > 1000000);
924dfdcada3SDoug Rabson }
925dfdcada3SDoug Rabson 
92674fb0ba7SJohn Baldwin int
927dfdcada3SDoug Rabson clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
928dfdcada3SDoug Rabson {
929dfdcada3SDoug Rabson 	struct ct_data *ct = (struct ct_data *) arg;
930dfdcada3SDoug Rabson 	struct uio uio;
9310b4f2ab0SRick Macklem 	struct mbuf *m, *m2;
932dfdcada3SDoug Rabson 	struct ct_request *cr;
933dfdcada3SDoug Rabson 	int error, rcvflag, foundreq;
934e2adc47dSRick Macklem 	uint32_t xid_plus_direction[2], header;
935e2adc47dSRick Macklem 	SVCXPRT *xprt;
936e2adc47dSRick Macklem 	struct cf_conn *cd;
9374302e8b6SRick Macklem 	u_int rawlen;
938ab0c29afSRick Macklem 	struct cmsghdr *cmsg;
939ab0c29afSRick Macklem 	struct tls_get_record tgr;
940ab0c29afSRick Macklem 
941ab0c29afSRick Macklem 	/*
942ab0c29afSRick Macklem 	 * RPC-over-TLS needs to block reception during
943ab0c29afSRick Macklem 	 * upcalls since the upcall will be doing I/O on
944ab0c29afSRick Macklem 	 * the socket via openssl library calls.
945ab0c29afSRick Macklem 	 */
946ab0c29afSRick Macklem 	mtx_lock(&ct->ct_lock);
947ab0c29afSRick Macklem 	if ((ct->ct_rcvstate & (RPCRCVSTATE_NORMAL |
948ab0c29afSRick Macklem 	    RPCRCVSTATE_NONAPPDATA)) == 0) {
949ab0c29afSRick Macklem 		/* Mark that a socket upcall needs to be done. */
950ab0c29afSRick Macklem 		if ((ct->ct_rcvstate & (RPCRCVSTATE_UPCALLNEEDED |
951ab0c29afSRick Macklem 		    RPCRCVSTATE_UPCALLINPROG)) != 0)
952ab0c29afSRick Macklem 			ct->ct_rcvstate |= RPCRCVSTATE_SOUPCALLNEEDED;
953ab0c29afSRick Macklem 		mtx_unlock(&ct->ct_lock);
954ab0c29afSRick Macklem 		return (SU_OK);
955ab0c29afSRick Macklem 	}
956ab0c29afSRick Macklem 	mtx_unlock(&ct->ct_lock);
957dfdcada3SDoug Rabson 
9584302e8b6SRick Macklem 	/*
9594302e8b6SRick Macklem 	 * If another thread is already here, it must be in
9604302e8b6SRick Macklem 	 * soreceive(), so just return to avoid races with it.
961e205fd31SGleb Smirnoff 	 * ct_upcallrefs is protected by the socket receive buffer lock
9624302e8b6SRick Macklem 	 * which is held in this function, except when
9634302e8b6SRick Macklem 	 * soreceive() is called.
9644302e8b6SRick Macklem 	 */
9654302e8b6SRick Macklem 	if (ct->ct_upcallrefs > 0)
9664302e8b6SRick Macklem 		return (SU_OK);
9673144f812SRick Macklem 	ct->ct_upcallrefs++;
9684302e8b6SRick Macklem 
9694302e8b6SRick Macklem 	/*
9704302e8b6SRick Macklem 	 * Read as much as possible off the socket and link it
9714302e8b6SRick Macklem 	 * onto ct_raw.
9724302e8b6SRick Macklem 	 */
9734302e8b6SRick Macklem 	for (;;) {
9744302e8b6SRick Macklem 		uio.uio_resid = 1000000000;
975dfdcada3SDoug Rabson 		uio.uio_td = curthread;
9764302e8b6SRick Macklem 		m2 = m = NULL;
977dfdcada3SDoug Rabson 		rcvflag = MSG_DONTWAIT | MSG_SOCALLBCK;
978ab0c29afSRick Macklem 		if (ct->ct_sslrefno != 0 && (ct->ct_rcvstate &
9790b4f2ab0SRick Macklem 		    RPCRCVSTATE_NORMAL) != 0)
980ab0c29afSRick Macklem 			rcvflag |= MSG_TLSAPPDATA;
981e205fd31SGleb Smirnoff 		SOCK_RECVBUF_UNLOCK(so);
9820b4f2ab0SRick Macklem 		error = soreceive(so, NULL, &uio, &m, &m2, &rcvflag);
983e205fd31SGleb Smirnoff 		SOCK_RECVBUF_LOCK(so);
984dfdcada3SDoug Rabson 
9854302e8b6SRick Macklem 		if (error == EWOULDBLOCK) {
986dfdcada3SDoug Rabson 			/*
9874302e8b6SRick Macklem 			 * We must re-test for readability after
9884302e8b6SRick Macklem 			 * taking the lock to protect us in the case
9894302e8b6SRick Macklem 			 * where a new packet arrives on the socket
9904302e8b6SRick Macklem 			 * after our call to soreceive fails with
9914302e8b6SRick Macklem 			 * EWOULDBLOCK.
992dfdcada3SDoug Rabson 			 */
9934302e8b6SRick Macklem 			error = 0;
9944302e8b6SRick Macklem 			if (!soreadable(so))
9954302e8b6SRick Macklem 				break;
9964302e8b6SRick Macklem 			continue;
9974302e8b6SRick Macklem 		}
9984302e8b6SRick Macklem 		if (error == 0 && m == NULL) {
999dfdcada3SDoug Rabson 			/*
1000dfdcada3SDoug Rabson 			 * We must have got EOF trying
1001dfdcada3SDoug Rabson 			 * to read from the stream.
1002dfdcada3SDoug Rabson 			 */
1003dfdcada3SDoug Rabson 			error = ECONNRESET;
1004dfdcada3SDoug Rabson 		}
1005ab0c29afSRick Macklem 
1006ab0c29afSRick Macklem 		/*
10070b4f2ab0SRick Macklem 		 * A return of ENXIO indicates that there is an
10080b4f2ab0SRick Macklem 		 * alert record at the head of the
1009ab0c29afSRick Macklem 		 * socket's receive queue, for TLS connections.
1010ab0c29afSRick Macklem 		 * This record needs to be handled in userland
1011ab0c29afSRick Macklem 		 * via an SSL_read() call, so do an upcall to the daemon.
1012ab0c29afSRick Macklem 		 */
1013ab0c29afSRick Macklem 		if (ct->ct_sslrefno != 0 && error == ENXIO) {
1014ab0c29afSRick Macklem 			/* Disable reception, marking an upcall needed. */
1015ab0c29afSRick Macklem 			mtx_lock(&ct->ct_lock);
1016ab0c29afSRick Macklem 			ct->ct_rcvstate |= RPCRCVSTATE_UPCALLNEEDED;
1017ab0c29afSRick Macklem 			/*
1018ab0c29afSRick Macklem 			 * If an upcall in needed, wake up the kthread
1019ab0c29afSRick Macklem 			 * that runs clnt_vc_dotlsupcall().
1020ab0c29afSRick Macklem 			 */
1021ab0c29afSRick Macklem 			wakeup(&ct->ct_sslrefno);
1022ab0c29afSRick Macklem 			mtx_unlock(&ct->ct_lock);
1023ab0c29afSRick Macklem 			break;
1024ab0c29afSRick Macklem 		}
10254302e8b6SRick Macklem 		if (error != 0)
1026dfdcada3SDoug Rabson 			break;
10274302e8b6SRick Macklem 
1028ab0c29afSRick Macklem 		/* Process any record header(s). */
1029ab0c29afSRick Macklem 		if (m2 != NULL) {
1030ab0c29afSRick Macklem 			cmsg = mtod(m2, struct cmsghdr *);
1031ab0c29afSRick Macklem 			if (cmsg->cmsg_type == TLS_GET_RECORD &&
1032ab0c29afSRick Macklem 			    cmsg->cmsg_len == CMSG_LEN(sizeof(tgr))) {
1033ab0c29afSRick Macklem 				memcpy(&tgr, CMSG_DATA(cmsg), sizeof(tgr));
1034ab0c29afSRick Macklem 				/*
10350b4f2ab0SRick Macklem 				 * TLS_RLTYPE_ALERT records should be handled
10360b4f2ab0SRick Macklem 				 * since soreceive() would have returned
10370b4f2ab0SRick Macklem 				 * ENXIO.  Just throw any other
10380b4f2ab0SRick Macklem 				 * non-TLS_RLTYPE_APP records away.
1039ab0c29afSRick Macklem 				 */
1040ab0c29afSRick Macklem 				if (tgr.tls_type != TLS_RLTYPE_APP) {
1041ab0c29afSRick Macklem 					m_freem(m);
1042ab0c29afSRick Macklem 					m_free(m2);
1043ab0c29afSRick Macklem 					mtx_lock(&ct->ct_lock);
1044ab0c29afSRick Macklem 					ct->ct_rcvstate &=
1045ab0c29afSRick Macklem 					    ~RPCRCVSTATE_NONAPPDATA;
1046ab0c29afSRick Macklem 					ct->ct_rcvstate |= RPCRCVSTATE_NORMAL;
1047ab0c29afSRick Macklem 					mtx_unlock(&ct->ct_lock);
1048ab0c29afSRick Macklem 					continue;
1049ab0c29afSRick Macklem 				}
1050ab0c29afSRick Macklem 			}
1051ab0c29afSRick Macklem 			m_free(m2);
1052ab0c29afSRick Macklem 		}
1053ab0c29afSRick Macklem 
10544302e8b6SRick Macklem 		if (ct->ct_raw != NULL)
10554302e8b6SRick Macklem 			m_last(ct->ct_raw)->m_next = m;
10564302e8b6SRick Macklem 		else
10574302e8b6SRick Macklem 			ct->ct_raw = m;
1058dfdcada3SDoug Rabson 	}
10594302e8b6SRick Macklem 	rawlen = m_length(ct->ct_raw, NULL);
10604302e8b6SRick Macklem 
10614302e8b6SRick Macklem 	/* Now, process as much of ct_raw as possible. */
10624302e8b6SRick Macklem 	for (;;) {
10634302e8b6SRick Macklem 		/*
10644302e8b6SRick Macklem 		 * If ct_record_resid is zero, we are waiting for a
10654302e8b6SRick Macklem 		 * record mark.
10664302e8b6SRick Macklem 		 */
10674302e8b6SRick Macklem 		if (ct->ct_record_resid == 0) {
10684302e8b6SRick Macklem 			if (rawlen < sizeof(uint32_t))
10694302e8b6SRick Macklem 				break;
10704302e8b6SRick Macklem 			m_copydata(ct->ct_raw, 0, sizeof(uint32_t),
10714302e8b6SRick Macklem 			    (char *)&header);
1072dfdcada3SDoug Rabson 			header = ntohl(header);
1073dfdcada3SDoug Rabson 			ct->ct_record_resid = header & 0x7fffffff;
1074dfdcada3SDoug Rabson 			ct->ct_record_eor = ((header & 0x80000000) != 0);
10754302e8b6SRick Macklem 			m_adj(ct->ct_raw, sizeof(uint32_t));
10764302e8b6SRick Macklem 			rawlen -= sizeof(uint32_t);
1077dfdcada3SDoug Rabson 		} else {
1078dfdcada3SDoug Rabson 			/*
10794302e8b6SRick Macklem 			 * Move as much of the record as possible to
10804302e8b6SRick Macklem 			 * ct_record.
1081a9148abdSDoug Rabson 			 */
10824302e8b6SRick Macklem 			if (rawlen == 0)
10833144f812SRick Macklem 				break;
10844302e8b6SRick Macklem 			if (rawlen <= ct->ct_record_resid) {
10854302e8b6SRick Macklem 				if (ct->ct_record != NULL)
10864302e8b6SRick Macklem 					m_last(ct->ct_record)->m_next =
10874302e8b6SRick Macklem 					    ct->ct_raw;
1088dfdcada3SDoug Rabson 				else
10894302e8b6SRick Macklem 					ct->ct_record = ct->ct_raw;
10904302e8b6SRick Macklem 				ct->ct_raw = NULL;
10914302e8b6SRick Macklem 				ct->ct_record_resid -= rawlen;
10924302e8b6SRick Macklem 				rawlen = 0;
10934302e8b6SRick Macklem 			} else {
10944302e8b6SRick Macklem 				m = m_split(ct->ct_raw, ct->ct_record_resid,
10954302e8b6SRick Macklem 				    M_NOWAIT);
10964302e8b6SRick Macklem 				if (m == NULL)
10974302e8b6SRick Macklem 					break;
10984302e8b6SRick Macklem 				if (ct->ct_record != NULL)
10994302e8b6SRick Macklem 					m_last(ct->ct_record)->m_next =
11004302e8b6SRick Macklem 					    ct->ct_raw;
11014302e8b6SRick Macklem 				else
11024302e8b6SRick Macklem 					ct->ct_record = ct->ct_raw;
11034302e8b6SRick Macklem 				rawlen -= ct->ct_record_resid;
11044302e8b6SRick Macklem 				ct->ct_record_resid = 0;
11054302e8b6SRick Macklem 				ct->ct_raw = m;
11064302e8b6SRick Macklem 			}
11074302e8b6SRick Macklem 			if (ct->ct_record_resid > 0)
11084302e8b6SRick Macklem 				break;
1109dfdcada3SDoug Rabson 
1110dfdcada3SDoug Rabson 			/*
1111dfdcada3SDoug Rabson 			 * If we have the entire record, see if we can
1112dfdcada3SDoug Rabson 			 * match it to a request.
1113dfdcada3SDoug Rabson 			 */
11144302e8b6SRick Macklem 			if (ct->ct_record_eor) {
1115dfdcada3SDoug Rabson 				/*
1116dfdcada3SDoug Rabson 				 * The XID is in the first uint32_t of
1117e2adc47dSRick Macklem 				 * the reply and the message direction
1118e2adc47dSRick Macklem 				 * is the second one.
1119dfdcada3SDoug Rabson 				 */
1120e2adc47dSRick Macklem 				if (ct->ct_record->m_len <
1121e2adc47dSRick Macklem 				    sizeof(xid_plus_direction) &&
11225e8eb3cdSRick Macklem 				    m_length(ct->ct_record, NULL) <
1123e2adc47dSRick Macklem 				    sizeof(xid_plus_direction)) {
11244302e8b6SRick Macklem 					/*
11254302e8b6SRick Macklem 					 * What to do now?
11264302e8b6SRick Macklem 					 * The data in the TCP stream is
11274302e8b6SRick Macklem 					 * corrupted such that there is no
11284302e8b6SRick Macklem 					 * valid RPC message to parse.
11294302e8b6SRick Macklem 					 * I think it best to close this
11304302e8b6SRick Macklem 					 * connection and allow
1131b94b9a80SRick Macklem 					 * clnt_reconnect_call() to try
11324302e8b6SRick Macklem 					 * and establish a new one.
11334302e8b6SRick Macklem 					 */
11344302e8b6SRick Macklem 					printf("clnt_vc_soupcall: "
11354302e8b6SRick Macklem 					    "connection data corrupted\n");
11364302e8b6SRick Macklem 					error = ECONNRESET;
11374302e8b6SRick Macklem 					goto wakeup_all;
11385e8eb3cdSRick Macklem 				}
1139e2adc47dSRick Macklem 				m_copydata(ct->ct_record, 0,
1140e2adc47dSRick Macklem 				    sizeof(xid_plus_direction),
1141e2adc47dSRick Macklem 				    (char *)xid_plus_direction);
1142e2adc47dSRick Macklem 				xid_plus_direction[0] =
1143e2adc47dSRick Macklem 				    ntohl(xid_plus_direction[0]);
1144e2adc47dSRick Macklem 				xid_plus_direction[1] =
1145e2adc47dSRick Macklem 				    ntohl(xid_plus_direction[1]);
1146e2adc47dSRick Macklem 				/* Check message direction. */
1147e2adc47dSRick Macklem 				if (xid_plus_direction[1] == CALL) {
1148e2adc47dSRick Macklem 					/* This is a backchannel request. */
1149e2adc47dSRick Macklem 					mtx_lock(&ct->ct_lock);
1150e2adc47dSRick Macklem 					xprt = ct->ct_backchannelxprt;
1151e2adc47dSRick Macklem 					if (xprt == NULL) {
1152e2adc47dSRick Macklem 						mtx_unlock(&ct->ct_lock);
1153e2adc47dSRick Macklem 						/* Just throw it away. */
1154e2adc47dSRick Macklem 						m_freem(ct->ct_record);
1155e2adc47dSRick Macklem 						ct->ct_record = NULL;
1156e2adc47dSRick Macklem 					} else {
1157e2adc47dSRick Macklem 						cd = (struct cf_conn *)
1158e2adc47dSRick Macklem 						    xprt->xp_p1;
1159e2adc47dSRick Macklem 						m2 = cd->mreq;
1160e2adc47dSRick Macklem 						/*
1161e2adc47dSRick Macklem 						 * The requests are chained
1162e2adc47dSRick Macklem 						 * in the m_nextpkt list.
1163e2adc47dSRick Macklem 						 */
1164e2adc47dSRick Macklem 						while (m2 != NULL &&
1165e2adc47dSRick Macklem 						    m2->m_nextpkt != NULL)
1166e2adc47dSRick Macklem 							/* Find end of list. */
1167e2adc47dSRick Macklem 							m2 = m2->m_nextpkt;
1168e2adc47dSRick Macklem 						if (m2 != NULL)
1169e2adc47dSRick Macklem 							m2->m_nextpkt =
1170e2adc47dSRick Macklem 							    ct->ct_record;
1171e2adc47dSRick Macklem 						else
1172e2adc47dSRick Macklem 							cd->mreq =
1173e2adc47dSRick Macklem 							    ct->ct_record;
1174e2adc47dSRick Macklem 						ct->ct_record->m_nextpkt =
1175e2adc47dSRick Macklem 						    NULL;
1176e2adc47dSRick Macklem 						ct->ct_record = NULL;
1177e2adc47dSRick Macklem 						xprt_active(xprt);
1178e2adc47dSRick Macklem 						mtx_unlock(&ct->ct_lock);
1179e2adc47dSRick Macklem 					}
1180e2adc47dSRick Macklem 				} else {
1181dfdcada3SDoug Rabson 					mtx_lock(&ct->ct_lock);
1182dfdcada3SDoug Rabson 					foundreq = 0;
1183e2adc47dSRick Macklem 					TAILQ_FOREACH(cr, &ct->ct_pending,
1184e2adc47dSRick Macklem 					    cr_link) {
1185e2adc47dSRick Macklem 						if (cr->cr_xid ==
1186e2adc47dSRick Macklem 						    xid_plus_direction[0]) {
1187dfdcada3SDoug Rabson 							/*
1188dfdcada3SDoug Rabson 							 * This one
1189c675522fSDoug Rabson 							 * matches. We leave
1190c675522fSDoug Rabson 							 * the reply mbuf in
1191dfdcada3SDoug Rabson 							 * cr->cr_mrep. Set
1192dfdcada3SDoug Rabson 							 * the XID to zero so
1193c675522fSDoug Rabson 							 * that we will ignore
1194e2adc47dSRick Macklem 							 * any duplicated
1195c675522fSDoug Rabson 							 * replies.
1196dfdcada3SDoug Rabson 							 */
1197dfdcada3SDoug Rabson 							cr->cr_xid = 0;
1198e2adc47dSRick Macklem 							cr->cr_mrep =
1199e2adc47dSRick Macklem 							    ct->ct_record;
1200dfdcada3SDoug Rabson 							cr->cr_error = 0;
1201dfdcada3SDoug Rabson 							foundreq = 1;
1202dfdcada3SDoug Rabson 							wakeup(cr);
1203dfdcada3SDoug Rabson 							break;
1204dfdcada3SDoug Rabson 						}
1205dfdcada3SDoug Rabson 					}
1206dfdcada3SDoug Rabson 					mtx_unlock(&ct->ct_lock);
1207dfdcada3SDoug Rabson 
1208dfdcada3SDoug Rabson 					if (!foundreq)
1209dfdcada3SDoug Rabson 						m_freem(ct->ct_record);
1210dfdcada3SDoug Rabson 					ct->ct_record = NULL;
1211dfdcada3SDoug Rabson 				}
1212dfdcada3SDoug Rabson 			}
1213e2adc47dSRick Macklem 		}
12144302e8b6SRick Macklem 	}
12154302e8b6SRick Macklem 
12164302e8b6SRick Macklem 	if (error != 0) {
12174302e8b6SRick Macklem 	wakeup_all:
12184302e8b6SRick Macklem 		/*
12194302e8b6SRick Macklem 		 * This socket is broken, so mark that it cannot
12204302e8b6SRick Macklem 		 * receive and fail all RPCs waiting for a reply
12214302e8b6SRick Macklem 		 * on it, so that they will be retried on a new
12224302e8b6SRick Macklem 		 * TCP connection created by clnt_reconnect_X().
12234302e8b6SRick Macklem 		 */
12244302e8b6SRick Macklem 		mtx_lock(&ct->ct_lock);
12254302e8b6SRick Macklem 		ct->ct_error.re_status = RPC_CANTRECV;
12264302e8b6SRick Macklem 		ct->ct_error.re_errno = error;
12274302e8b6SRick Macklem 		TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) {
12284302e8b6SRick Macklem 			cr->cr_error = error;
12294302e8b6SRick Macklem 			wakeup(cr);
12304302e8b6SRick Macklem 		}
12314302e8b6SRick Macklem 		mtx_unlock(&ct->ct_lock);
12324302e8b6SRick Macklem 	}
12334302e8b6SRick Macklem 
12343144f812SRick Macklem 	ct->ct_upcallrefs--;
12353144f812SRick Macklem 	if (ct->ct_upcallrefs < 0)
12363144f812SRick Macklem 		panic("rpcvc upcall refcnt");
12373144f812SRick Macklem 	if (ct->ct_upcallrefs == 0)
12383144f812SRick Macklem 		wakeup(&ct->ct_upcallrefs);
123974fb0ba7SJohn Baldwin 	return (SU_OK);
1240dfdcada3SDoug Rabson }
12413144f812SRick Macklem 
12423144f812SRick Macklem /*
12433144f812SRick Macklem  * Wait for all upcalls in progress to complete.
12443144f812SRick Macklem  */
12453144f812SRick Macklem static void
12463144f812SRick Macklem clnt_vc_upcallsdone(struct ct_data *ct)
12473144f812SRick Macklem {
12483144f812SRick Macklem 
1249e205fd31SGleb Smirnoff 	SOCK_RECVBUF_LOCK_ASSERT(ct->ct_socket);
12503144f812SRick Macklem 
12513144f812SRick Macklem 	while (ct->ct_upcallrefs > 0)
12523144f812SRick Macklem 		(void) msleep(&ct->ct_upcallrefs,
12533144f812SRick Macklem 		    SOCKBUF_MTX(&ct->ct_socket->so_rcv), 0, "rpcvcup", 0);
12543144f812SRick Macklem }
1255ab0c29afSRick Macklem 
1256ab0c29afSRick Macklem /*
1257ab0c29afSRick Macklem  * Do a TLS upcall to the rpctlscd daemon, as required.
1258ab0c29afSRick Macklem  * This function runs as a kthread.
1259ab0c29afSRick Macklem  */
1260ab0c29afSRick Macklem static void
1261ab0c29afSRick Macklem clnt_vc_dotlsupcall(void *data)
1262ab0c29afSRick Macklem {
12634ba444deSRick Macklem 	CLIENT *cl = (CLIENT *)data;
12644ba444deSRick Macklem 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
1265ab0c29afSRick Macklem 	enum clnt_stat ret;
1266ab0c29afSRick Macklem 	uint32_t reterr;
1267ab0c29afSRick Macklem 
1268b2ff4cb1SGleb Smirnoff 	CURVNET_SET(ct->ct_socket->so_vnet);
1269ab0c29afSRick Macklem 	mtx_lock(&ct->ct_lock);
1270ab0c29afSRick Macklem 	ct->ct_rcvstate |= RPCRCVSTATE_UPCALLTHREAD;
1271ab0c29afSRick Macklem 	while (!ct->ct_closed) {
1272ab0c29afSRick Macklem 		if ((ct->ct_rcvstate & RPCRCVSTATE_UPCALLNEEDED) != 0) {
1273ab0c29afSRick Macklem 			ct->ct_rcvstate &= ~RPCRCVSTATE_UPCALLNEEDED;
1274ab0c29afSRick Macklem 			ct->ct_rcvstate |= RPCRCVSTATE_UPCALLINPROG;
1275ab0c29afSRick Macklem 			if (ct->ct_sslrefno != 0 && ct->ct_sslrefno !=
1276ab0c29afSRick Macklem 			    RPCTLS_REFNO_HANDSHAKE) {
1277ab0c29afSRick Macklem 				mtx_unlock(&ct->ct_lock);
1278ab0c29afSRick Macklem 				ret = rpctls_cl_handlerecord(ct->ct_sslsec,
1279ab0c29afSRick Macklem 				    ct->ct_sslusec, ct->ct_sslrefno, &reterr);
1280ab0c29afSRick Macklem 				mtx_lock(&ct->ct_lock);
1281ab0c29afSRick Macklem 			}
1282ab0c29afSRick Macklem 			ct->ct_rcvstate &= ~RPCRCVSTATE_UPCALLINPROG;
1283ab0c29afSRick Macklem 			if (ret == RPC_SUCCESS && reterr == RPCTLSERR_OK)
1284ab0c29afSRick Macklem 				ct->ct_rcvstate |= RPCRCVSTATE_NORMAL;
1285ab0c29afSRick Macklem 			else
1286ab0c29afSRick Macklem 				ct->ct_rcvstate |= RPCRCVSTATE_NONAPPDATA;
1287ab0c29afSRick Macklem 			wakeup(&ct->ct_rcvstate);
1288ab0c29afSRick Macklem 		}
1289ab0c29afSRick Macklem 		if ((ct->ct_rcvstate & RPCRCVSTATE_SOUPCALLNEEDED) != 0) {
1290ab0c29afSRick Macklem 			ct->ct_rcvstate &= ~RPCRCVSTATE_SOUPCALLNEEDED;
1291ab0c29afSRick Macklem 			mtx_unlock(&ct->ct_lock);
1292e205fd31SGleb Smirnoff 			SOCK_RECVBUF_LOCK(ct->ct_socket);
1293ab0c29afSRick Macklem 			clnt_vc_soupcall(ct->ct_socket, ct, M_NOWAIT);
1294e205fd31SGleb Smirnoff 			SOCK_RECVBUF_UNLOCK(ct->ct_socket);
1295ab0c29afSRick Macklem 			mtx_lock(&ct->ct_lock);
1296ab0c29afSRick Macklem 		}
1297ab0c29afSRick Macklem 		msleep(&ct->ct_sslrefno, &ct->ct_lock, 0, "clntvcdu", hz);
1298ab0c29afSRick Macklem 	}
1299ab0c29afSRick Macklem 	ct->ct_rcvstate &= ~RPCRCVSTATE_UPCALLTHREAD;
1300ab0c29afSRick Macklem 	wakeup(&ct->ct_sslrefno);
1301ab0c29afSRick Macklem 	mtx_unlock(&ct->ct_lock);
13024ba444deSRick Macklem 	CLNT_RELEASE(cl);
1303b2ff4cb1SGleb Smirnoff 	CURVNET_RESTORE();
1304*6a876e97SGleb Smirnoff 	kthread_exit();
1305ab0c29afSRick Macklem }
1306