1/*	$NetBSD: svc_vc.c,v 1.7 2000/08/03 00:01:53 fvdl Exp $	*/
2
3/*-
4 * Copyright (c) 2009, Sun Microsystems, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice,
10 *   this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 *   this list of conditions and the following disclaimer in the documentation
13 *   and/or other materials provided with the distribution.
14 * - Neither the name of Sun Microsystems, Inc. nor the names of its
15 *   contributors may be used to endorse or promote products derived
16 *   from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#if defined(LIBC_SCCS) && !defined(lint)
32static char *sccsid2 = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
33static char *sccsid = "@(#)svc_tcp.c	2.2 88/08/01 4.0 RPCSRC";
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38/*
39 * svc_vc.c, Server side for Connection Oriented based RPC.
40 *
41 * Actually implements two flavors of transporter -
42 * a tcp rendezvouser (a listner and connection establisher)
43 * and a record/tcp stream.
44 */
45
46#include "namespace.h"
47#include "reentrant.h"
48#include <sys/param.h>
49#include <sys/poll.h>
50#include <sys/socket.h>
51#include <sys/un.h>
52#include <sys/time.h>
53#include <sys/uio.h>
54#include <netinet/in.h>
55#include <netinet/tcp.h>
56
57#include <assert.h>
58#include <err.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65
66#include <rpc/rpc.h>
67
68#include "rpc_com.h"
69#include "mt_misc.h"
70#include "un-namespace.h"
71
72static SVCXPRT *makefd_xprt(int, u_int, u_int);
73static bool_t rendezvous_request(SVCXPRT *, struct rpc_msg *);
74static enum xprt_stat rendezvous_stat(SVCXPRT *);
75static void svc_vc_destroy(SVCXPRT *);
76static void __svc_vc_dodestroy (SVCXPRT *);
77static int read_vc(void *, void *, int);
78static int write_vc(void *, void *, int);
79static enum xprt_stat svc_vc_stat(SVCXPRT *);
80static bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *);
81static bool_t svc_vc_getargs(SVCXPRT *, xdrproc_t, void *);
82static bool_t svc_vc_freeargs(SVCXPRT *, xdrproc_t, void *);
83static bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *);
84static void svc_vc_rendezvous_ops(SVCXPRT *);
85static void svc_vc_ops(SVCXPRT *);
86static bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in);
87static bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq,
88				   	     void *in);
89
90struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */
91	u_int sendsize;
92	u_int recvsize;
93	int maxrec;
94};
95
96struct cf_conn {  /* kept in xprt->xp_p1 for actual connection */
97	enum xprt_stat strm_stat;
98	u_int32_t x_id;
99	XDR xdrs;
100	char verf_body[MAX_AUTH_BYTES];
101	u_int sendsize;
102	u_int recvsize;
103	int maxrec;
104	bool_t nonblock;
105	struct timeval last_recv_time;
106};
107
108/*
109 * Usage:
110 *	xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
111 *
112 * Creates, registers, and returns a (rpc) tcp based transporter.
113 * Once *xprt is initialized, it is registered as a transporter
114 * see (svc.h, xprt_register).  This routine returns
115 * a NULL if a problem occurred.
116 *
117 * The filedescriptor passed in is expected to refer to a bound, but
118 * not yet connected socket.
119 *
120 * Since streams do buffered io similar to stdio, the caller can specify
121 * how big the send and receive buffers are via the second and third parms;
122 * 0 => use the system default.
123 */
124SVCXPRT *
125svc_vc_create(int fd, u_int sendsize, u_int recvsize)
126{
127	SVCXPRT *xprt = NULL;
128	struct cf_rendezvous *r = NULL;
129	struct __rpc_sockinfo si;
130	struct sockaddr_storage sslocal;
131	socklen_t slen;
132
133	if (!__rpc_fd2sockinfo(fd, &si))
134		return NULL;
135
136	r = mem_alloc(sizeof(*r));
137	if (r == NULL) {
138		warnx("svc_vc_create: out of memory");
139		goto cleanup_svc_vc_create;
140	}
141	r->sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
142	r->recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
143	r->maxrec = __svc_maxrec;
144	xprt = svc_xprt_alloc();
145	if (xprt == NULL) {
146		warnx("svc_vc_create: out of memory");
147		goto cleanup_svc_vc_create;
148	}
149	xprt->xp_p1 = r;
150	xprt->xp_verf = _null_auth;
151	svc_vc_rendezvous_ops(xprt);
152	xprt->xp_port = (u_short)-1;	/* It is the rendezvouser */
153	xprt->xp_fd = fd;
154
155	slen = sizeof (struct sockaddr_storage);
156	if (_getsockname(fd, (struct sockaddr *)(void *)&sslocal, &slen) < 0) {
157		warnx("svc_vc_create: could not retrieve local addr");
158		goto cleanup_svc_vc_create;
159	}
160
161	xprt->xp_ltaddr.maxlen = xprt->xp_ltaddr.len = sslocal.ss_len;
162	xprt->xp_ltaddr.buf = mem_alloc((size_t)sslocal.ss_len);
163	if (xprt->xp_ltaddr.buf == NULL) {
164		warnx("svc_vc_create: no mem for local addr");
165		goto cleanup_svc_vc_create;
166	}
167	memcpy(xprt->xp_ltaddr.buf, &sslocal, (size_t)sslocal.ss_len);
168
169	xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
170	xprt_register(xprt);
171	return (xprt);
172cleanup_svc_vc_create:
173	if (xprt)
174		mem_free(xprt, sizeof(*xprt));
175	if (r != NULL)
176		mem_free(r, sizeof(*r));
177	return (NULL);
178}
179
180/*
181 * Like svtcp_create(), except the routine takes any *open* UNIX file
182 * descriptor as its first input.
183 */
184SVCXPRT *
185svc_fd_create(int fd, u_int sendsize, u_int recvsize)
186{
187	struct sockaddr_storage ss;
188	socklen_t slen;
189	SVCXPRT *ret;
190
191	assert(fd != -1);
192
193	ret = makefd_xprt(fd, sendsize, recvsize);
194	if (ret == NULL)
195		return NULL;
196
197	slen = sizeof (struct sockaddr_storage);
198	if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
199		warnx("svc_fd_create: could not retrieve local addr");
200		goto freedata;
201	}
202	ret->xp_ltaddr.maxlen = ret->xp_ltaddr.len = ss.ss_len;
203	ret->xp_ltaddr.buf = mem_alloc((size_t)ss.ss_len);
204	if (ret->xp_ltaddr.buf == NULL) {
205		warnx("svc_fd_create: no mem for local addr");
206		goto freedata;
207	}
208	memcpy(ret->xp_ltaddr.buf, &ss, (size_t)ss.ss_len);
209
210	slen = sizeof (struct sockaddr_storage);
211	if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
212		warnx("svc_fd_create: could not retrieve remote addr");
213		goto freedata;
214	}
215	ret->xp_rtaddr.maxlen = ret->xp_rtaddr.len = ss.ss_len;
216	ret->xp_rtaddr.buf = mem_alloc((size_t)ss.ss_len);
217	if (ret->xp_rtaddr.buf == NULL) {
218		warnx("svc_fd_create: no mem for local addr");
219		goto freedata;
220	}
221	memcpy(ret->xp_rtaddr.buf, &ss, (size_t)ss.ss_len);
222#ifdef PORTMAP
223	if (ss.ss_family == AF_INET || ss.ss_family == AF_LOCAL) {
224		ret->xp_raddr = *(struct sockaddr_in *)ret->xp_rtaddr.buf;
225		ret->xp_addrlen = sizeof (struct sockaddr_in);
226	}
227#endif				/* PORTMAP */
228
229	return ret;
230
231freedata:
232	if (ret->xp_ltaddr.buf != NULL)
233		mem_free(ret->xp_ltaddr.buf, rep->xp_ltaddr.maxlen);
234
235	return NULL;
236}
237
238static SVCXPRT *
239makefd_xprt(int fd, u_int sendsize, u_int recvsize)
240{
241	SVCXPRT *xprt;
242	struct cf_conn *cd;
243	const char *netid;
244	struct __rpc_sockinfo si;
245
246	assert(fd != -1);
247
248	xprt = svc_xprt_alloc();
249	if (xprt == NULL) {
250		warnx("svc_vc: makefd_xprt: out of memory");
251		goto done;
252	}
253	cd = mem_alloc(sizeof(struct cf_conn));
254	if (cd == NULL) {
255		warnx("svc_tcp: makefd_xprt: out of memory");
256		svc_xprt_free(xprt);
257		xprt = NULL;
258		goto done;
259	}
260	cd->strm_stat = XPRT_IDLE;
261	xdrrec_create(&(cd->xdrs), sendsize, recvsize,
262	    xprt, read_vc, write_vc);
263	xprt->xp_p1 = cd;
264	xprt->xp_verf.oa_base = cd->verf_body;
265	svc_vc_ops(xprt);  /* truly deals with calls */
266	xprt->xp_port = 0;  /* this is a connection, not a rendezvouser */
267	xprt->xp_fd = fd;
268        if (__rpc_fd2sockinfo(fd, &si) && __rpc_sockinfo2netid(&si, &netid))
269		xprt->xp_netid = strdup(netid);
270
271	xprt_register(xprt);
272done:
273	return (xprt);
274}
275
276/*ARGSUSED*/
277static bool_t
278rendezvous_request(SVCXPRT *xprt, struct rpc_msg *msg)
279{
280	int sock, flags;
281	struct cf_rendezvous *r;
282	struct cf_conn *cd;
283	struct sockaddr_storage addr, sslocal;
284	socklen_t len, slen;
285	struct __rpc_sockinfo si;
286	SVCXPRT *newxprt;
287	fd_set cleanfds;
288
289	assert(xprt != NULL);
290	assert(msg != NULL);
291
292	r = (struct cf_rendezvous *)xprt->xp_p1;
293again:
294	len = sizeof addr;
295	if ((sock = _accept(xprt->xp_fd, (struct sockaddr *)(void *)&addr,
296	    &len)) < 0) {
297		if (errno == EINTR)
298			goto again;
299		/*
300		 * Clean out the most idle file descriptor when we're
301		 * running out.
302		 */
303		if (errno == EMFILE || errno == ENFILE) {
304			cleanfds = svc_fdset;
305			__svc_clean_idle(&cleanfds, 0, FALSE);
306			goto again;
307		}
308		return (FALSE);
309	}
310	/*
311	 * make a new transporter (re-uses xprt)
312	 */
313	newxprt = makefd_xprt(sock, r->sendsize, r->recvsize);
314	newxprt->xp_rtaddr.buf = mem_alloc(len);
315	if (newxprt->xp_rtaddr.buf == NULL)
316		return (FALSE);
317	memcpy(newxprt->xp_rtaddr.buf, &addr, len);
318	newxprt->xp_rtaddr.len = len;
319#ifdef PORTMAP
320	if (addr.ss_family == AF_INET || addr.ss_family == AF_LOCAL) {
321		newxprt->xp_raddr = *(struct sockaddr_in *)newxprt->xp_rtaddr.buf;
322		newxprt->xp_addrlen = sizeof (struct sockaddr_in);
323	}
324#endif				/* PORTMAP */
325	if (__rpc_fd2sockinfo(sock, &si) && si.si_proto == IPPROTO_TCP) {
326		len = 1;
327		/* XXX fvdl - is this useful? */
328		_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &len, sizeof (len));
329	}
330
331	cd = (struct cf_conn *)newxprt->xp_p1;
332
333	cd->recvsize = r->recvsize;
334	cd->sendsize = r->sendsize;
335	cd->maxrec = r->maxrec;
336
337	if (cd->maxrec != 0) {
338		flags = _fcntl(sock, F_GETFL, 0);
339		if (flags  == -1)
340			return (FALSE);
341		if (_fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
342			return (FALSE);
343		if (cd->recvsize > cd->maxrec)
344			cd->recvsize = cd->maxrec;
345		cd->nonblock = TRUE;
346		__xdrrec_setnonblock(&cd->xdrs, cd->maxrec);
347	} else
348		cd->nonblock = FALSE;
349	slen = sizeof(struct sockaddr_storage);
350	if(_getsockname(sock, (struct sockaddr *)(void *)&sslocal, &slen) < 0) {
351		warnx("svc_vc_create: could not retrieve local addr");
352		newxprt->xp_ltaddr.maxlen = newxprt->xp_ltaddr.len = 0;
353	} else {
354		newxprt->xp_ltaddr.maxlen = newxprt->xp_ltaddr.len = sslocal.ss_len;
355		newxprt->xp_ltaddr.buf = mem_alloc((size_t)sslocal.ss_len);
356		if (newxprt->xp_ltaddr.buf == NULL) {
357			warnx("svc_vc_create: no mem for local addr");
358			newxprt->xp_ltaddr.maxlen = newxprt->xp_ltaddr.len = 0;
359		} else {
360			memcpy(newxprt->xp_ltaddr.buf, &sslocal, (size_t)sslocal.ss_len);
361		}
362	}
363
364	gettimeofday(&cd->last_recv_time, NULL);
365
366	return (FALSE); /* there is never an rpc msg to be processed */
367}
368
369/*ARGSUSED*/
370static enum xprt_stat
371rendezvous_stat(SVCXPRT *xprt)
372{
373
374	return (XPRT_IDLE);
375}
376
377static void
378svc_vc_destroy(SVCXPRT *xprt)
379{
380	assert(xprt != NULL);
381
382	xprt_unregister(xprt);
383	__svc_vc_dodestroy(xprt);
384}
385
386static void
387__svc_vc_dodestroy(SVCXPRT *xprt)
388{
389	struct cf_conn *cd;
390	struct cf_rendezvous *r;
391
392	cd = (struct cf_conn *)xprt->xp_p1;
393
394	if (xprt->xp_fd != RPC_ANYFD)
395		(void)_close(xprt->xp_fd);
396	if (xprt->xp_port != 0) {
397		/* a rendezvouser socket */
398		r = (struct cf_rendezvous *)xprt->xp_p1;
399		mem_free(r, sizeof (struct cf_rendezvous));
400		xprt->xp_port = 0;
401	} else {
402		/* an actual connection socket */
403		XDR_DESTROY(&(cd->xdrs));
404		mem_free(cd, sizeof(struct cf_conn));
405	}
406	if (xprt->xp_rtaddr.buf)
407		mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
408	if (xprt->xp_ltaddr.buf)
409		mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
410	free(xprt->xp_tp);
411	free(xprt->xp_netid);
412	svc_xprt_free(xprt);
413}
414
415/*ARGSUSED*/
416static bool_t
417svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in)
418{
419	return (FALSE);
420}
421
422static bool_t
423svc_vc_rendezvous_control(SVCXPRT *xprt, const u_int rq, void *in)
424{
425	struct cf_rendezvous *cfp;
426
427	cfp = (struct cf_rendezvous *)xprt->xp_p1;
428	if (cfp == NULL)
429		return (FALSE);
430	switch (rq) {
431		case SVCGET_CONNMAXREC:
432			*(int *)in = cfp->maxrec;
433			break;
434		case SVCSET_CONNMAXREC:
435			cfp->maxrec = *(int *)in;
436			break;
437		default:
438			return (FALSE);
439	}
440	return (TRUE);
441}
442
443/*
444 * reads data from the tcp or uip connection.
445 * any error is fatal and the connection is closed.
446 * (And a read of zero bytes is a half closed stream => error.)
447 * All read operations timeout after 35 seconds.  A timeout is
448 * fatal for the connection.
449 */
450static int
451read_vc(void *xprtp, void *buf, int len)
452{
453	SVCXPRT *xprt;
454	int sock;
455	int milliseconds = 35 * 1000;
456	struct pollfd pollfd;
457	struct cf_conn *cfp;
458
459	xprt = (SVCXPRT *)xprtp;
460	assert(xprt != NULL);
461
462	sock = xprt->xp_fd;
463
464	cfp = (struct cf_conn *)xprt->xp_p1;
465
466	if (cfp->nonblock) {
467		len = _read(sock, buf, (size_t)len);
468		if (len < 0) {
469			if (errno == EAGAIN)
470				len = 0;
471			else
472				goto fatal_err;
473		}
474		if (len != 0)
475			gettimeofday(&cfp->last_recv_time, NULL);
476		return len;
477	}
478
479	do {
480		pollfd.fd = sock;
481		pollfd.events = POLLIN;
482		pollfd.revents = 0;
483		switch (_poll(&pollfd, 1, milliseconds)) {
484		case -1:
485			if (errno == EINTR)
486				continue;
487			/*FALLTHROUGH*/
488		case 0:
489			goto fatal_err;
490
491		default:
492			break;
493		}
494	} while ((pollfd.revents & POLLIN) == 0);
495
496	if ((len = _read(sock, buf, (size_t)len)) > 0) {
497		gettimeofday(&cfp->last_recv_time, NULL);
498		return (len);
499	}
500
501fatal_err:
502	((struct cf_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
503	return (-1);
504}
505
506/*
507 * writes data to the tcp connection.
508 * Any error is fatal and the connection is closed.
509 */
510static int
511write_vc(void *xprtp, void *buf, int len)
512{
513	SVCXPRT *xprt;
514	int i, cnt;
515	struct cf_conn *cd;
516	struct timeval tv0, tv1;
517
518	xprt = (SVCXPRT *)xprtp;
519	assert(xprt != NULL);
520
521	cd = (struct cf_conn *)xprt->xp_p1;
522
523	if (cd->nonblock)
524		gettimeofday(&tv0, NULL);
525
526	for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) {
527		i = _write(xprt->xp_fd, buf, (size_t)cnt);
528		if (i  < 0) {
529			if (errno != EAGAIN || !cd->nonblock) {
530				cd->strm_stat = XPRT_DIED;
531				return (-1);
532			}
533			if (cd->nonblock) {
534				/*
535				 * For non-blocking connections, do not
536				 * take more than 2 seconds writing the
537				 * data out.
538				 *
539				 * XXX 2 is an arbitrary amount.
540				 */
541				gettimeofday(&tv1, NULL);
542				if (tv1.tv_sec - tv0.tv_sec >= 2) {
543					cd->strm_stat = XPRT_DIED;
544					return (-1);
545				}
546			}
547			i = 0;
548		}
549	}
550
551	return (len);
552}
553
554static enum xprt_stat
555svc_vc_stat(SVCXPRT *xprt)
556{
557	struct cf_conn *cd;
558
559	assert(xprt != NULL);
560
561	cd = (struct cf_conn *)(xprt->xp_p1);
562
563	if (cd->strm_stat == XPRT_DIED)
564		return (XPRT_DIED);
565	if (! xdrrec_eof(&(cd->xdrs)))
566		return (XPRT_MOREREQS);
567	return (XPRT_IDLE);
568}
569
570static bool_t
571svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
572{
573	struct cf_conn *cd;
574	XDR *xdrs;
575
576	assert(xprt != NULL);
577	assert(msg != NULL);
578
579	cd = (struct cf_conn *)(xprt->xp_p1);
580	xdrs = &(cd->xdrs);
581
582	if (cd->nonblock) {
583		if (!__xdrrec_getrec(xdrs, &cd->strm_stat, TRUE))
584			return FALSE;
585	} else {
586		(void)xdrrec_skiprecord(xdrs);
587	}
588
589	xdrs->x_op = XDR_DECODE;
590	if (xdr_callmsg(xdrs, msg)) {
591		cd->x_id = msg->rm_xid;
592		return (TRUE);
593	}
594	cd->strm_stat = XPRT_DIED;
595	return (FALSE);
596}
597
598static bool_t
599svc_vc_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
600{
601	struct cf_conn *cd;
602
603	assert(xprt != NULL);
604	cd = (struct cf_conn *)(xprt->xp_p1);
605	return (SVCAUTH_UNWRAP(&SVC_AUTH(xprt),
606		&cd->xdrs, xdr_args, args_ptr));
607}
608
609static bool_t
610svc_vc_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
611{
612	XDR *xdrs;
613
614	assert(xprt != NULL);
615	/* args_ptr may be NULL */
616
617	xdrs = &(((struct cf_conn *)(xprt->xp_p1))->xdrs);
618
619	xdrs->x_op = XDR_FREE;
620	return ((*xdr_args)(xdrs, args_ptr));
621}
622
623static bool_t
624svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg)
625{
626	struct cf_conn *cd;
627	XDR *xdrs;
628	bool_t rstat;
629	xdrproc_t xdr_proc;
630	caddr_t xdr_where;
631	u_int pos;
632
633	assert(xprt != NULL);
634	assert(msg != NULL);
635
636	cd = (struct cf_conn *)(xprt->xp_p1);
637	xdrs = &(cd->xdrs);
638
639	xdrs->x_op = XDR_ENCODE;
640	msg->rm_xid = cd->x_id;
641	rstat = TRUE;
642	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
643	    msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
644		xdr_proc = msg->acpted_rply.ar_results.proc;
645		xdr_where = msg->acpted_rply.ar_results.where;
646		msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
647		msg->acpted_rply.ar_results.where = NULL;
648
649		pos = XDR_GETPOS(xdrs);
650		if (!xdr_replymsg(xdrs, msg) ||
651		    !SVCAUTH_WRAP(&SVC_AUTH(xprt), xdrs, xdr_proc, xdr_where)) {
652			XDR_SETPOS(xdrs, pos);
653			rstat = FALSE;
654		}
655	} else {
656		rstat = xdr_replymsg(xdrs, msg);
657	}
658
659	if (rstat)
660		(void)xdrrec_endofrecord(xdrs, TRUE);
661
662	return (rstat);
663}
664
665static void
666svc_vc_ops(SVCXPRT *xprt)
667{
668	static struct xp_ops ops;
669	static struct xp_ops2 ops2;
670
671/* VARIABLES PROTECTED BY ops_lock: ops, ops2 */
672
673	mutex_lock(&ops_lock);
674	if (ops.xp_recv == NULL) {
675		ops.xp_recv = svc_vc_recv;
676		ops.xp_stat = svc_vc_stat;
677		ops.xp_getargs = svc_vc_getargs;
678		ops.xp_reply = svc_vc_reply;
679		ops.xp_freeargs = svc_vc_freeargs;
680		ops.xp_destroy = svc_vc_destroy;
681		ops2.xp_control = svc_vc_control;
682	}
683	xprt->xp_ops = &ops;
684	xprt->xp_ops2 = &ops2;
685	mutex_unlock(&ops_lock);
686}
687
688static void
689svc_vc_rendezvous_ops(SVCXPRT *xprt)
690{
691	static struct xp_ops ops;
692	static struct xp_ops2 ops2;
693
694	mutex_lock(&ops_lock);
695	if (ops.xp_recv == NULL) {
696		ops.xp_recv = rendezvous_request;
697		ops.xp_stat = rendezvous_stat;
698		ops.xp_getargs =
699		    (bool_t (*)(SVCXPRT *, xdrproc_t, void *))abort;
700		ops.xp_reply =
701		    (bool_t (*)(SVCXPRT *, struct rpc_msg *))abort;
702		ops.xp_freeargs =
703		    (bool_t (*)(SVCXPRT *, xdrproc_t, void *))abort;
704		ops.xp_destroy = svc_vc_destroy;
705		ops2.xp_control = svc_vc_rendezvous_control;
706	}
707	xprt->xp_ops = &ops;
708	xprt->xp_ops2 = &ops2;
709	mutex_unlock(&ops_lock);
710}
711
712/*
713 * Get the effective UID of the sending process. Used by rpcbind, keyserv
714 * and rpc.yppasswdd on AF_LOCAL.
715 */
716int
717__rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) {
718	int sock, ret;
719	gid_t egid;
720	uid_t euid;
721	struct sockaddr *sa;
722
723	sock = transp->xp_fd;
724	sa = (struct sockaddr *)transp->xp_rtaddr.buf;
725	if (sa->sa_family == AF_LOCAL) {
726		ret = getpeereid(sock, &euid, &egid);
727		if (ret == 0)
728			*uid = euid;
729		return (ret);
730	} else
731		return (-1);
732}
733
734/*
735 * Destroy xprts that have not have had any activity in 'timeout' seconds.
736 * If 'cleanblock' is true, blocking connections (the default) are also
737 * cleaned. If timeout is 0, the least active connection is picked.
738 */
739bool_t
740__svc_clean_idle(fd_set *fds, int timeout, bool_t cleanblock)
741{
742	int i, ncleaned;
743	SVCXPRT *xprt, *least_active;
744	struct timeval tv, tdiff, tmax;
745	struct cf_conn *cd;
746
747	gettimeofday(&tv, NULL);
748	tmax.tv_sec = tmax.tv_usec = 0;
749	least_active = NULL;
750	rwlock_wrlock(&svc_fd_lock);
751	for (i = ncleaned = 0; i <= svc_maxfd; i++) {
752		if (FD_ISSET(i, fds)) {
753			xprt = __svc_xports[i];
754			if (xprt == NULL || xprt->xp_ops == NULL ||
755			    xprt->xp_ops->xp_recv != svc_vc_recv)
756				continue;
757			cd = (struct cf_conn *)xprt->xp_p1;
758			if (!cleanblock && !cd->nonblock)
759				continue;
760			if (timeout == 0) {
761				timersub(&tv, &cd->last_recv_time, &tdiff);
762				if (timercmp(&tdiff, &tmax, >)) {
763					tmax = tdiff;
764					least_active = xprt;
765				}
766				continue;
767			}
768			if (tv.tv_sec - cd->last_recv_time.tv_sec > timeout) {
769				__xprt_unregister_unlocked(xprt);
770				__svc_vc_dodestroy(xprt);
771				ncleaned++;
772			}
773		}
774	}
775	if (timeout == 0 && least_active != NULL) {
776		__xprt_unregister_unlocked(least_active);
777		__svc_vc_dodestroy(least_active);
778		ncleaned++;
779	}
780	rwlock_unlock(&svc_fd_lock);
781	return ncleaned > 0 ? TRUE : FALSE;
782}
783