Mon Jul 26 15:56:46 2010 UTC ()
Add libc rpc bits modified to be suitable for testing the nfs
server & client.


(pooka)
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/Makefile.inc
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/bindresvport.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/clnt_bcast.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/clnt_dg.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/clnt_generic.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/clnt_vc.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/namespace.h
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/reentrant.h
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/rpc_generic.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/rpc_internal.h
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/rpc_soc.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/rpcb_clnt.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc_dg.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc_dg.h
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc_fdset.h
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc_generic.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc_run.c
diff -r0 -r1.1 src/tests/fs/common/nfsrpc/svc_vc.c

File Added: src/tests/fs/common/nfsrpc/Attic/Makefile.inc
#	$NetBSD: Makefile.inc,v 1.1 2010/07/26 15:56:45 pooka Exp $
#

.PATH:	${.PARSEDIR}

# libc rpc using rump syscalls
SRCS+=	bindresvport.c clnt_bcast.c clnt_dg.c clnt_generic.c clnt_vc.c \
	rpc_generic.c rpc_soc.c rpcb_clnt.c \
	svc.c svc_dg.c svc_generic.c svc_run.c svc_vc.c

CPPFLAGS+=	-DPORTMAP -D_REENTRANT -DRUMP_SYS_NETWORKING
CPPFLAGS+=	-DDEBUG -DLIBWRAP

File Added: src/tests/fs/common/nfsrpc/Attic/bindresvport.c
/*	$NetBSD: bindresvport.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char *sccsid = "@(#)bindresvport.c 1.8 88/02/08 SMI";
static char *sccsid = "@(#)bindresvport.c	2.2 88/07/29 4.0 RPCSRC";
#else
__RCSID("$NetBSD: bindresvport.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * Copyright (c) 1987 by Sun Microsystems, Inc.
 */

#include "namespace.h"

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <rpc/rpc.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

/*
 * Bind a socket to a privileged IP port
 */
int
bindresvport(sd, brsin)
	int sd;
	struct sockaddr_in *brsin;
{
	return bindresvport_sa(sd, (struct sockaddr *)(void *)brsin);
}

#include <stdio.h>
/*
 * Bind a socket to a privileged IP port
 */
int
bindresvport_sa(sd, sa)
	int sd;
	struct sockaddr *sa;
{
	int error, old;
	struct sockaddr_storage myaddr;
	struct sockaddr_in *brsin;
#ifdef INET6
	struct sockaddr_in6 *brsin6;
#endif
	int proto, portrange, portlow;
	u_int16_t *portp;
	socklen_t salen;
	int af;

	if (sa == NULL) {
		salen = sizeof(myaddr);
		sa = (struct sockaddr *)(void *)&myaddr;

		if (getsockname(sd, sa, &salen) == -1)
			return -1;	/* errno is correctly set */

		af = sa->sa_family;
		memset(sa, 0, salen);
	} else
		af = sa->sa_family;

	switch (af) {
	case AF_INET:
		proto = IPPROTO_IP;
		portrange = IP_PORTRANGE;
		portlow = IP_PORTRANGE_LOW;
		brsin = (struct sockaddr_in *)(void *)sa;
		salen = sizeof(struct sockaddr_in);
		portp = &brsin->sin_port;
		break;
#ifdef INET6
	case AF_INET6:
		proto = IPPROTO_IPV6;
		portrange = IPV6_PORTRANGE;
		portlow = IPV6_PORTRANGE_LOW;
		brsin6 = (struct sockaddr_in6 *)(void *)sa;
		salen = sizeof(struct sockaddr_in6);
		portp = &brsin6->sin6_port;
		break;
#endif
	default:
		errno = EPFNOSUPPORT;
		return (-1);
	}
	sa->sa_family = af;
	sa->sa_len = salen;

	if (*portp == 0) {
		socklen_t oldlen = sizeof(old);

		error = getsockopt(sd, proto, portrange, &old, &oldlen);
		if (error < 0)
			return (error);
		error = setsockopt(sd, proto, portrange, &portlow,
		    sizeof(portlow));
		if (error < 0)
			return (error);
	}

	error = bind(sd, sa, salen);

	if (*portp == 0) {
		int saved_errno = errno;

		if (error < 0) {
			if (setsockopt(sd, proto, portrange, &old,
			    sizeof(old)) < 0)
				errno = saved_errno;
			return (error);
		}

		if (sa != (struct sockaddr *)(void *)&myaddr) {
			/* What did the kernel assign? */
			if (getsockname(sd, sa, &salen) < 0)
				errno = saved_errno;
			return (error);
		}
	}
	return (error);
}

File Added: src/tests/fs/common/nfsrpc/Attic/clnt_bcast.c
/*	$NetBSD: clnt_bcast.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
 */

/* #ident	"@(#)clnt_bcast.c	1.18	94/05/03 SMI" */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)clnt_bcast.c 1.15 89/04/21 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: clnt_bcast.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * clnt_bcast.c
 * Client interface to broadcast service.
 *
 * Copyright (C) 1988, Sun Microsystems, Inc.
 *
 * The following is kludged-up support for simple rpc broadcasts.
 * Someday a large, complicated system will replace these routines.
 */

#include "namespace.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <net/if.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <sys/poll.h>
#include <rpc/rpc.h>
#ifdef PORTMAP
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_rmt.h>
#endif
#include <rpc/nettype.h>
#include <arpa/inet.h>
#ifdef RPC_DEBUG
#include <stdio.h>
#endif
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <err.h>
#include <string.h>
#include <stdio.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#include "rpc_internal.h"

#define	MAXBCAST 20	/* Max no of broadcasting transports */
#define	INITTIME 4000	/* Time to wait initially */
#define	WAITTIME 8000	/* Maximum time to wait */

/*
 * If nettype is NULL, it broadcasts on all the available
 * datagram_n transports. May potentially lead to broadacst storms
 * and hence should be used with caution, care and courage.
 *
 * The current parameter xdr packet size is limited by the max tsdu
 * size of the transport. If the max tsdu size of any transport is
 * smaller than the parameter xdr packet, then broadcast is not
 * sent on that transport.
 *
 * Also, the packet size should be less the packet size of
 * the data link layer (for ethernet it is 1400 bytes).  There is
 * no easy way to find out the max size of the data link layer and
 * we are assuming that the args would be smaller than that.
 *
 * The result size has to be smaller than the transport tsdu size.
 *
 * If PORTMAP has been defined, we send two packets for UDP, one for
 * rpcbind and one for portmap. For those machines which support
 * both rpcbind and portmap, it will cause them to reply twice, and
 * also here it will get two responses ... inefficient and clumsy.
 */

struct broadif {
	int index;
	struct sockaddr_storage broadaddr;
	TAILQ_ENTRY(broadif) link;
};

typedef TAILQ_HEAD(, broadif) broadlist_t;

int __rpc_getbroadifs __P((int, int, int, broadlist_t *));
void __rpc_freebroadifs __P((broadlist_t *));
int __rpc_broadenable __P((int, int, struct broadif *));

int __rpc_lowvers = 0;

int
__rpc_getbroadifs(int af, int proto, int socktype, broadlist_t *list)
{
	int count = 0;
	struct broadif *bip;
	struct ifaddrs *ifap, *ifp;
#ifdef INET6
	struct sockaddr_in6 *sin6;
#endif
	struct sockaddr_in *gbsin;
	struct addrinfo hints, *res;

	_DIAGASSERT(list != NULL);

	if (getifaddrs(&ifp) < 0)
		return 0;

	memset(&hints, 0, sizeof hints);

	hints.ai_family = af;
	hints.ai_protocol = proto;
	hints.ai_socktype = socktype;

	if (getaddrinfo(NULL, "sunrpc", &hints, &res) != 0) {
		freeifaddrs(ifp);
		return 0;
	}

	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
		if (ifap->ifa_addr->sa_family != af ||
		    !(ifap->ifa_flags & IFF_UP))
			continue;
		bip = malloc(sizeof(*bip));
		if (bip == NULL)
			break;
		bip->index = if_nametoindex(ifap->ifa_name);
		if (
#ifdef INET6
		    af != AF_INET6 &&
#endif
		    (ifap->ifa_flags & IFF_BROADCAST) &&
		    ifap->ifa_broadaddr) {
			memcpy(&bip->broadaddr, ifap->ifa_broadaddr,
			    (size_t)ifap->ifa_broadaddr->sa_len);
			gbsin = (struct sockaddr_in *)(void *)&bip->broadaddr;
			gbsin->sin_port =
			    ((struct sockaddr_in *)
			    (void *)res->ai_addr)->sin_port;
		} else
#ifdef INET6
		if (af == AF_INET6 && (ifap->ifa_flags & IFF_MULTICAST)) {
			sin6 = (struct sockaddr_in6 *)(void *)&bip->broadaddr;
			inet_pton(af, RPCB_MULTICAST_ADDR, &sin6->sin6_addr);
			sin6->sin6_family = af;
			sin6->sin6_len = sizeof *sin6;
			sin6->sin6_port =
			    ((struct sockaddr_in6 *)
			    (void *)res->ai_addr)->sin6_port;
			sin6->sin6_scope_id = bip->index;
		} else
#endif
		{
			free(bip);
			continue;
		}
		TAILQ_INSERT_TAIL(list, bip, link);
		count++;
	}
	freeifaddrs(ifp);
	freeaddrinfo(res);

	return count;
}

void
__rpc_freebroadifs(broadlist_t *list)
{
	struct broadif *bip, *next;

	_DIAGASSERT(list != NULL);

	bip = TAILQ_FIRST(list);

	while (bip != NULL) {
		next = TAILQ_NEXT(bip, link);
		free(bip);
		bip = next;
	}
}

int
/*ARGSUSED*/
__rpc_broadenable(int af, int s, struct broadif *bip)
{
	int o = 1;

#if 0
	_DIAGASSERT(bip != NULL);

	if (af == AF_INET6) {
		fprintf(stderr, "set v6 multicast if to %d\n", bip->index);
		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &bip->index,
		    sizeof bip->index) < 0)
			return -1;
	} else
#endif
		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &o, sizeof o) < 0)
			return -1;

	return 0;
}


enum clnt_stat
rpc_broadcast_exp(prog, vers, proc, xargs, argsp, xresults, resultsp,
	eachresult, inittime, waittime, nettype)
	rpcprog_t	prog;		/* program number */
	rpcvers_t	vers;		/* version number */
	rpcproc_t	proc;		/* procedure number */
	xdrproc_t	xargs;		/* xdr routine for args */
	const char *	argsp;		/* pointer to args */
	xdrproc_t	xresults;	/* xdr routine for results */
	caddr_t		resultsp;	/* pointer to results */
	resultproc_t	eachresult;	/* call with each result obtained */
	int 		inittime;	/* how long to wait initially */
	int 		waittime;	/* maximum time to wait */
	const char		*nettype;	/* transport type */
{
	enum clnt_stat	stat = RPC_SUCCESS; /* Return status */
	XDR 		xdr_stream; /* XDR stream */
	XDR 		*xdrs = &xdr_stream;
	struct rpc_msg	msg;	/* RPC message */
	char 		*outbuf = NULL;	/* Broadcast msg buffer */
	char		*inbuf = NULL; /* Reply buf */
	int		inlen;
	u_int 		maxbufsize = 0;
	AUTH 		*sys_auth = authunix_create_default();
	int		i;
	void		*handle;
	char		uaddress[1024];	/* A self imposed limit */
	char		*uaddrp = uaddress;
	int 		pmap_reply_flag; /* reply recvd from PORTMAP */
	/* An array of all the suitable broadcast transports */
	struct {
		int fd;		/* File descriptor */
		int af;
		int proto;
		struct netconfig *nconf; /* Netconfig structure */
		u_int asize;	/* Size of the addr buf */
		u_int dsize;	/* Size of the data buf */
		struct sockaddr_storage raddr; /* Remote address */
		broadlist_t nal;
	} fdlist[MAXBCAST];
	struct pollfd pfd[MAXBCAST];
	size_t fdlistno = 0;
	struct r_rpcb_rmtcallargs barg;	/* Remote arguments */
	struct r_rpcb_rmtcallres bres; /* Remote results */
	size_t outlen;
	struct netconfig *nconf;
	int msec;
	int pollretval;
	int fds_found;
	struct timespec ts;

#ifdef PORTMAP
	size_t outlen_pmap = 0;
	u_long port;		/* Remote port number */
	int pmap_flag = 0;	/* UDP exists ? */
	char *outbuf_pmap = NULL;
	struct rmtcallargs barg_pmap;	/* Remote arguments */
	struct rmtcallres bres_pmap; /* Remote results */
	u_int udpbufsz = 0;
#endif				/* PORTMAP */

	if (sys_auth == NULL) {
		return (RPC_SYSTEMERROR);
	}
	/*
	 * initialization: create a fd, a broadcast address, and send the
	 * request on the broadcast transport.
	 * Listen on all of them and on replies, call the user supplied
	 * function.
	 */

	if (nettype == NULL)
		nettype = "datagram_n";
	if ((handle = __rpc_setconf(nettype)) == NULL) {
		AUTH_DESTROY(sys_auth);
		return (RPC_UNKNOWNPROTO);
	}
	while ((nconf = __rpc_getconf(handle)) != NULL) {
		int fd;
		struct __rpc_sockinfo si;

		if (nconf->nc_semantics != NC_TPI_CLTS)
			continue;
		if (fdlistno >= MAXBCAST)
			break;	/* No more slots available */
		if (!__rpc_nconf2sockinfo(nconf, &si))
			continue;

		TAILQ_INIT(&fdlist[fdlistno].nal);
		if (__rpc_getbroadifs(si.si_af, si.si_proto, si.si_socktype, 
		    &fdlist[fdlistno].nal) == 0)
			continue;

		fd = socket(si.si_af, si.si_socktype, si.si_proto);
		if (fd < 0) {
			stat = RPC_CANTSEND;
			continue;
		}
		fdlist[fdlistno].af = si.si_af;
		fdlist[fdlistno].proto = si.si_proto;
		fdlist[fdlistno].fd = fd;
		fdlist[fdlistno].nconf = nconf;
		fdlist[fdlistno].asize = __rpc_get_a_size(si.si_af);
		pfd[fdlistno].events = POLLIN | POLLPRI |
			POLLRDNORM | POLLRDBAND;
		pfd[fdlistno].fd = fdlist[fdlistno].fd = fd;
		fdlist[fdlistno].dsize = __rpc_get_t_size(si.si_af, si.si_proto,
							  0);

		if (maxbufsize <= fdlist[fdlistno].dsize)
			maxbufsize = fdlist[fdlistno].dsize;
			
#ifdef PORTMAP
		if (si.si_af == AF_INET && si.si_proto == IPPROTO_UDP) {
			udpbufsz = fdlist[fdlistno].dsize;
			if ((outbuf_pmap = malloc(udpbufsz)) == NULL) {
				rump_sys_close(fd);
				stat = RPC_SYSTEMERROR;
				goto done_broad;
			}
			pmap_flag = 1;
		}
#endif
		fdlistno++;
	}

	if (fdlistno == 0) {
		if (stat == RPC_SUCCESS)
			stat = RPC_UNKNOWNPROTO;
		goto done_broad;
	}
	if (maxbufsize == 0) {
		if (stat == RPC_SUCCESS)
			stat = RPC_CANTSEND;
		goto done_broad;
	}
	inbuf = malloc(maxbufsize);
	outbuf = malloc(maxbufsize);
	if ((inbuf == NULL) || (outbuf == NULL)) {
		stat = RPC_SYSTEMERROR;
		goto done_broad;
	}

	/* Serialize all the arguments which have to be sent */
	msg.rm_xid = __RPC_GETXID();
	msg.rm_direction = CALL;
	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
	msg.rm_call.cb_prog = RPCBPROG;
	msg.rm_call.cb_vers = RPCBVERS;
	msg.rm_call.cb_proc = RPCBPROC_CALLIT;
	barg.prog = prog;
	barg.vers = vers;
	barg.proc = proc;
	barg.args.args_val = argsp;
	barg.xdr_args = xargs;
	bres.addr = uaddrp;
	bres.results.results_val = resultsp;
	bres.xdr_res = xresults;
	msg.rm_call.cb_cred = sys_auth->ah_cred;
	msg.rm_call.cb_verf = sys_auth->ah_verf;
	xdrmem_create(xdrs, outbuf, maxbufsize, XDR_ENCODE);
	if ((!xdr_callmsg(xdrs, &msg)) ||
	    (!xdr_rpcb_rmtcallargs(xdrs,
	    (struct rpcb_rmtcallargs *)(void *)&barg))) {
		stat = RPC_CANTENCODEARGS;
		goto done_broad;
	}
	outlen = xdr_getpos(xdrs);
	xdr_destroy(xdrs);

#ifdef PORTMAP
	/* Prepare the packet for version 2 PORTMAP */
	if (pmap_flag) {
		msg.rm_xid++;	/* One way to distinguish */
		msg.rm_call.cb_prog = PMAPPROG;
		msg.rm_call.cb_vers = PMAPVERS;
		msg.rm_call.cb_proc = PMAPPROC_CALLIT;
		barg_pmap.prog = prog;
		barg_pmap.vers = vers;
		barg_pmap.proc = proc;
		barg_pmap.args_ptr = argsp;
		barg_pmap.xdr_args = xargs;
		bres_pmap.port_ptr = &port;
		bres_pmap.xdr_results = xresults;
		bres_pmap.results_ptr = resultsp;
		xdrmem_create(xdrs, outbuf_pmap, udpbufsz, XDR_ENCODE);
		if ((! xdr_callmsg(xdrs, &msg)) ||
		    (! xdr_rmtcall_args(xdrs, &barg_pmap))) {
			stat = RPC_CANTENCODEARGS;
			goto done_broad;
		}
		outlen_pmap = xdr_getpos(xdrs);
		xdr_destroy(xdrs);
	}
#endif				/* PORTMAP */

	/*
	 * Basic loop: broadcast the packets to transports which
	 * support data packets of size such that one can encode
	 * all the arguments.
	 * Wait a while for response(s).
	 * The response timeout grows larger per iteration.
	 */
	for (msec = inittime; msec <= waittime; msec += msec) {
		struct broadif *bip;

		/* Broadcast all the packets now */
		for (i = 0; i < fdlistno; i++) {
			if (fdlist[i].dsize < outlen) {
				stat = RPC_CANTSEND;
				continue;
			}
			for (bip = TAILQ_FIRST(&fdlist[i].nal); bip != NULL;
			     bip = TAILQ_NEXT(bip, link)) {
				void *addr;

				addr = &bip->broadaddr;

				__rpc_broadenable(fdlist[i].af, fdlist[i].fd,
				    bip);

				/*
				 * Only use version 3 if lowvers is not set
				 */

				if (!__rpc_lowvers)
					if (sendto(fdlist[i].fd, outbuf,
					    outlen, 0, (struct sockaddr*)addr,
					    (size_t)fdlist[i].asize) !=
					    outlen) {
						warn("clnt_bcast: cannot send"
						      " broadcast packet");
						stat = RPC_CANTSEND;
						continue;
					};
#ifdef RPC_DEBUG
				if (!__rpc_lowvers)
					fprintf(stderr, "Broadcast packet sent "
						"for %s\n",
						 fdlist[i].nconf->nc_netid);
#endif
#ifdef PORTMAP
				/*
				 * Send the version 2 packet also
				 * for UDP/IP
				 */
				if (pmap_flag && fdlist[i].proto == IPPROTO_UDP) {
					if (sendto(fdlist[i].fd, outbuf_pmap,
					    outlen_pmap, 0, addr,
					    (size_t)fdlist[i].asize) !=
						outlen_pmap) {
						warnx("clnt_bcast: "
				"Cannot send broadcast packet");
						stat = RPC_CANTSEND;
						continue;
					}
				}
#ifdef RPC_DEBUG
				fprintf(stderr, "PMAP Broadcast packet "
					"sent for %s\n",
					fdlist[i].nconf->nc_netid);
#endif
#endif				/* PORTMAP */
			}
			/* End for sending all packets on this transport */
		}	/* End for sending on all transports */

		if (eachresult == NULL) {
			stat = RPC_SUCCESS;
			goto done_broad;
		}

		/*
		 * Get all the replies from these broadcast requests
		 */
	recv_again:
		ts.tv_sec = msec / 1000;
		ts.tv_nsec = (msec % 1000) * 1000000;

		switch (pollretval = rump_sys_pollts(pfd, fdlistno, &ts, NULL)) {
		case 0:		/* timed out */
			stat = RPC_TIMEDOUT;
			continue;
		case -1:	/* some kind of error - we ignore it */
			goto recv_again;
		}		/* end of poll results switch */

		for (i = fds_found = 0;
		     i < fdlistno && fds_found < pollretval; i++) {
			bool_t done = FALSE;

			if (pfd[i].revents == 0)
				continue;
			else if (pfd[i].revents & POLLNVAL) {
				/*
				 * Something bad has happened to this descri-
				 * ptor. We can cause pollts() to ignore
				 * it simply by using a negative fd.  We do that
				 * rather than compacting the pfd[] and fdlist[]
				 * arrays.
				 */
				pfd[i].fd = -1;
				fds_found++;
				continue;
			} else
				fds_found++;
#ifdef RPC_DEBUG
			fprintf(stderr, "response for %s\n",
				fdlist[i].nconf->nc_netid);
#endif
		try_again:
			inlen = recvfrom(fdlist[i].fd, inbuf, fdlist[i].dsize,
			    0, (struct sockaddr *)(void *)&fdlist[i].raddr,
			    &fdlist[i].asize);
			if (inlen < 0) {
				if (errno == EINTR)
					goto try_again;
				warnx("clnt_bcast: Cannot receive reply to "
					"broadcast");
				stat = RPC_CANTRECV;
				continue;
			}
			if (inlen < sizeof (u_int32_t))
				continue; /* Drop that and go ahead */
			/*
			 * see if reply transaction id matches sent id.
			 * If so, decode the results. If return id is xid + 1
			 * it was a PORTMAP reply
			 */
			if (*((u_int32_t *)(void *)(inbuf)) ==
			    *((u_int32_t *)(void *)(outbuf))) {
				pmap_reply_flag = 0;
				msg.acpted_rply.ar_verf = _null_auth;
				msg.acpted_rply.ar_results.where =
					(caddr_t)(void *)&bres;
				msg.acpted_rply.ar_results.proc =
					(xdrproc_t)xdr_rpcb_rmtcallres;
#ifdef PORTMAP
			} else if (pmap_flag &&
				*((u_int32_t *)(void *)(inbuf)) ==
				*((u_int32_t *)(void *)(outbuf_pmap))) {
				pmap_reply_flag = 1;
				msg.acpted_rply.ar_verf = _null_auth;
				msg.acpted_rply.ar_results.where =
					(caddr_t)(void *)&bres_pmap;
				msg.acpted_rply.ar_results.proc =
					(xdrproc_t)xdr_rmtcallres;
#endif				/* PORTMAP */
			} else
				continue;
			xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE);
			if (xdr_replymsg(xdrs, &msg)) {
				if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
				    (msg.acpted_rply.ar_stat == SUCCESS)) {
					struct netbuf taddr, *np;
					struct sockaddr_in *bsin;

#ifdef PORTMAP
					if (pmap_flag && pmap_reply_flag) {
						bsin = (struct sockaddr_in *)
						    (void *)&fdlist[i].raddr;
						bsin->sin_port =
						    htons((u_short)port);
						taddr.len = taddr.maxlen = 
						    fdlist[i].raddr.ss_len;
						taddr.buf = &fdlist[i].raddr;
						done = (*eachresult)(resultsp,
						    &taddr, fdlist[i].nconf);
					} else {
#endif
#ifdef RPC_DEBUG
						fprintf(stderr, "uaddr %s\n",
						    uaddrp);
#endif
						np = uaddr2taddr(
						    fdlist[i].nconf, uaddrp);
						done = (*eachresult)(resultsp,
						    np, fdlist[i].nconf);
						free(np);
#ifdef PORTMAP
					}
#endif
				}
				/* otherwise, we just ignore the errors ... */
			}
			/* else some kind of deserialization problem ... */

			xdrs->x_op = XDR_FREE;
			msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
			(void) xdr_replymsg(xdrs, &msg);
			(void) (*xresults)(xdrs, resultsp);
			XDR_DESTROY(xdrs);
			if (done) {
				stat = RPC_SUCCESS;
				goto done_broad;
			} else {
				goto recv_again;
			}
		}		/* The recv for loop */
	}			/* The giant for loop */

done_broad:
	if (inbuf)
		(void) free(inbuf);
	if (outbuf)
		(void) free(outbuf);
#ifdef PORTMAP
	if (outbuf_pmap)
		(void) free(outbuf_pmap);
#endif
	for (i = 0; i < fdlistno; i++) {
		(void) rump_sys_close(fdlist[i].fd);
		__rpc_freebroadifs(&fdlist[i].nal);
	}
	AUTH_DESTROY(sys_auth);
	(void) __rpc_endconf(handle);

	return (stat);
}


enum clnt_stat
rpc_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp,
			eachresult, nettype)
	rpcprog_t	prog;		/* program number */
	rpcvers_t	vers;		/* version number */
	rpcproc_t	proc;		/* procedure number */
	xdrproc_t	xargs;		/* xdr routine for args */
	const char *	argsp;		/* pointer to args */
	xdrproc_t	xresults;	/* xdr routine for results */
	caddr_t		resultsp;	/* pointer to results */
	resultproc_t	eachresult;	/* call with each result obtained */
	const char		*nettype;	/* transport type */
{
	enum clnt_stat	dummy;

	dummy = rpc_broadcast_exp(prog, vers, proc, xargs, argsp,
		xresults, resultsp, eachresult,
		INITTIME, WAITTIME, nettype);
	return (dummy);
}

File Added: src/tests/fs/common/nfsrpc/Attic/clnt_dg.c
/*	$NetBSD: clnt_dg.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
 */

/* #ident	"@(#)clnt_dg.c	1.23	94/04/22 SMI" */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: clnt_dg.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * Implements a connectionless client side RPC.
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <rpc/rpc.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <err.h>
#include <stdio.h>
#include "rpc_internal.h"

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#define	RPC_MAX_BACKOFF		30 /* seconds */


static struct clnt_ops *clnt_dg_ops __P((void));
static bool_t time_not_ok __P((struct timeval *));
static enum clnt_stat clnt_dg_call __P((CLIENT *, rpcproc_t, xdrproc_t,
    const char *, xdrproc_t, caddr_t, struct timeval));
static void clnt_dg_geterr __P((CLIENT *, struct rpc_err *));
static bool_t clnt_dg_freeres __P((CLIENT *, xdrproc_t, caddr_t));
static void clnt_dg_abort __P((CLIENT *));
static bool_t clnt_dg_control __P((CLIENT *, u_int, char *));
static void clnt_dg_destroy __P((CLIENT *));




/*
 *	This machinery implements per-fd locks for MT-safety.  It is not
 *	sufficient to do per-CLIENT handle locks for MT-safety because a
 *	user may create more than one CLIENT handle with the same fd behind
 *	it.  Therfore, we allocate an array of flags (dg_fd_locks), protected
 *	by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
 *	similarly protected.  Dg_fd_lock[fd] == 1 => a call is activte on some
 *	CLIENT handle created for that fd.
 *	The current implementation holds locks across the entire RPC and reply,
 *	including retransmissions.  Yes, this is silly, and as soon as this
 *	code is proven to work, this should be the first thing fixed.  One step
 *	at a time.
 */
static int	*dg_fd_locks;
#ifdef _REENTRANT
extern int __isthreaded;
#define __rpc_lock_value __isthreaded;
extern mutex_t clnt_fd_lock;
static cond_t	*dg_cv;
#define	release_fd_lock(fd, mask) {		\
	mutex_lock(&clnt_fd_lock);	\
	dg_fd_locks[fd] = 0;		\
	mutex_unlock(&clnt_fd_lock);	\
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);	\
	cond_signal(&dg_cv[fd]);	\
}
#else
#define release_fd_lock(fd,mask)
#define __rpc_lock_value 0
#endif

static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";

/* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */

/*
 * Private data kept per client handle
 */
struct cu_data {
	int			cu_fd;		/* connections fd */
	bool_t			cu_closeit;	/* opened by library */
	struct sockaddr_storage	cu_raddr;	/* remote address */
	int			cu_rlen;
	struct timeval		cu_wait;	/* retransmit interval */
	struct timeval		cu_total;	/* total time for the call */
	struct rpc_err		cu_error;
	XDR			cu_outxdrs;
	u_int			cu_xdrpos;
	u_int			cu_sendsz;	/* send size */
	char			*cu_outbuf;
	u_int			cu_recvsz;	/* recv size */
	struct pollfd		cu_pfdp;
	char			cu_inbuf[1];
};

/*
 * Connection less client creation returns with client handle parameters.
 * Default options are set, which the user can change using clnt_control().
 * fd should be open and bound.
 * NB: The rpch->cl_auth is initialized to null authentication.
 * 	Caller may wish to set this something more useful.
 *
 * sendsz and recvsz are the maximum allowable packet sizes that can be
 * sent and received. Normally they are the same, but they can be
 * changed to improve the program efficiency and buffer allocation.
 * If they are 0, use the transport default.
 *
 * If svcaddr is NULL, returns NULL.
 */
CLIENT *
clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
	int fd;				/* open file descriptor */
	const struct netbuf *svcaddr;	/* servers address */
	rpcprog_t program;		/* program number */
	rpcvers_t version;		/* version number */
	u_int sendsz;			/* buffer recv size */
	u_int recvsz;			/* buffer send size */
{
	CLIENT *cl = NULL;		/* client handle */
	struct cu_data *cu = NULL;	/* private data */
	struct rpc_msg call_msg;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;
	struct __rpc_sockinfo si;
	int one = 1;

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
	if (dg_fd_locks == NULL) {
#ifdef _REENTRANT
		size_t cv_allocsz;
#endif
		size_t fd_allocsz;
		int dtbsize = __rpc_dtbsize();

		fd_allocsz = dtbsize * sizeof (int);
		dg_fd_locks = mem_alloc(fd_allocsz);
		if (dg_fd_locks == NULL) {
			mutex_unlock(&clnt_fd_lock);
			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
			goto err1;
		} else
			memset(dg_fd_locks, '\0', fd_allocsz);

#ifdef _REENTRANT
		cv_allocsz = dtbsize * sizeof (cond_t);
		dg_cv = mem_alloc(cv_allocsz);
		if (dg_cv == NULL) {
			mem_free(dg_fd_locks, fd_allocsz);
			dg_fd_locks = NULL;
			mutex_unlock(&clnt_fd_lock);
			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
			goto err1;
		} else {
			int i;

			for (i = 0; i < dtbsize; i++)
				cond_init(&dg_cv[i], 0, (void *) 0);
		}
#endif
	}

	mutex_unlock(&clnt_fd_lock);
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);

	if (svcaddr == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
		return (NULL);
	}

	if (!__rpc_fd2sockinfo(fd, &si)) {
		rpc_createerr.cf_stat = RPC_TLIERROR;
		rpc_createerr.cf_error.re_errno = 0;
		return (NULL);
	}
	/*
	 * Find the receive and the send size
	 */
	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
	if ((sendsz == 0) || (recvsz == 0)) {
		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
		rpc_createerr.cf_error.re_errno = 0;
		return (NULL);
	}

	if ((cl = mem_alloc(sizeof (CLIENT))) == NULL)
		goto err1;
	/*
	 * Should be multiple of 4 for XDR.
	 */
	sendsz = ((sendsz + 3) / 4) * 4;
	recvsz = ((recvsz + 3) / 4) * 4;
	cu = malloc(sizeof (*cu) + sendsz + recvsz);
	if (cu == NULL)
		goto err1;
	memset(cu, 0, sizeof(*cu));
	(void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len);
	cu->cu_rlen = svcaddr->len;
	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
	/* Other values can also be set through clnt_control() */
#if 0
	cu->cu_wait.tv_sec = 15;	/* heuristically chosen */
#endif
	cu->cu_wait.tv_sec = 0;		/* for testing, 10x / second */
	cu->cu_wait.tv_usec = 100000;
	cu->cu_total.tv_sec = -1;
	cu->cu_total.tv_usec = -1;
	cu->cu_sendsz = sendsz;
	cu->cu_recvsz = recvsz;
	call_msg.rm_xid = __RPC_GETXID();
	call_msg.rm_call.cb_prog = program;
	call_msg.rm_call.cb_vers = version;
	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
	if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
		rpc_createerr.cf_error.re_errno = 0;
		goto err2;
	}
	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));

	/* XXX fvdl - do we still want this? */
#if 0
	(void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf);
#endif
	rump_sys_ioctl(fd, FIONBIO, (char *)(void *)&one);

	/*
	 * By default, closeit is always FALSE. It is users responsibility
	 * to do a close on it, else the user may use clnt_control
	 * to let clnt_destroy do it for him/her.
	 */
	cu->cu_closeit = FALSE;
	cu->cu_fd = fd;
	cu->cu_pfdp.fd = cu->cu_fd;
	cu->cu_pfdp.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
	cl->cl_ops = clnt_dg_ops();
	cl->cl_private = (caddr_t)(void *)cu;
	cl->cl_auth = authnone_create();
	cl->cl_tp = NULL;
	cl->cl_netid = NULL;
	return (cl);
err1:
	warnx(mem_err_clnt_dg);
	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
	rpc_createerr.cf_error.re_errno = errno;
err2:
	if (cl) {
		mem_free(cl, sizeof (CLIENT));
		if (cu)
			mem_free(cu, sizeof (*cu) + sendsz + recvsz);
	}
	return (NULL);
}

static enum clnt_stat
clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
	CLIENT	*cl;			/* client handle */
	rpcproc_t	proc;		/* procedure number */
	xdrproc_t	xargs;		/* xdr routine for args */
	const char *	argsp;		/* pointer to args */
	xdrproc_t	xresults;	/* xdr routine for results */
	caddr_t		resultsp;	/* pointer to results */
	struct timeval	utimeout;	/* seconds to wait before giving up */
{
	struct cu_data *cu;
	XDR *xdrs;
	size_t outlen;
	struct rpc_msg reply_msg;
	XDR reply_xdrs;
	bool_t ok;
	int nrefreshes = 2;		/* number of times to refresh cred */
	struct timeval timeout;
	struct timeval retransmit_time;
	struct timeval next_sendtime, starttime, time_waited, tv;
#ifdef _REENTRANT
	sigset_t mask, *maskp = &mask;
#else
	sigset_t *maskp = NULL;
#endif
	sigset_t newmask;
	ssize_t recvlen = 0;
	struct timespec ts;
	int n;

	_DIAGASSERT(cl != NULL);

	cu = (struct cu_data *)cl->cl_private;

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
	while (dg_fd_locks[cu->cu_fd])
		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
	dg_fd_locks[cu->cu_fd] = __rpc_lock_value;
	mutex_unlock(&clnt_fd_lock);
	if (cu->cu_total.tv_usec == -1) {
		timeout = utimeout;	/* use supplied timeout */
	} else {
		timeout = cu->cu_total;	/* use default timeout */
	}

	time_waited.tv_sec = 0;
	time_waited.tv_usec = 0;
	retransmit_time = next_sendtime = cu->cu_wait;
	gettimeofday(&starttime, NULL);

call_again:
	xdrs = &(cu->cu_outxdrs);
	xdrs->x_op = XDR_ENCODE;
	XDR_SETPOS(xdrs, cu->cu_xdrpos);
	/*
	 * the transaction is the first thing in the out buffer
	 */
	(*(u_int32_t *)(void *)(cu->cu_outbuf))++;
	if ((! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
	    (! (*xargs)(xdrs, __UNCONST(argsp)))) {
		cu->cu_error.re_status = RPC_CANTENCODEARGS;
		goto out;
	}
	outlen = (size_t)XDR_GETPOS(xdrs);

send_again:
	if (sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0,
	    (struct sockaddr *)(void *)&cu->cu_raddr, (socklen_t)cu->cu_rlen)
	    != outlen) {
		cu->cu_error.re_errno = errno;
		cu->cu_error.re_status = RPC_CANTSEND;
		goto out;
	}

	/*
	 * Hack to provide rpc-based message passing
	 */
	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
		cu->cu_error.re_status = RPC_TIMEDOUT;
		goto out;
	}
	/*
	 * sub-optimal code appears here because we have
	 * some clock time to spare while the packets are in flight.
	 * (We assume that this is actually only executed once.)
	 */
	reply_msg.acpted_rply.ar_verf = _null_auth;
	reply_msg.acpted_rply.ar_results.where = resultsp;
	reply_msg.acpted_rply.ar_results.proc = xresults;


	for (;;) {
		/* Decide how long to wait. */
		if (timercmp(&next_sendtime, &timeout, <))
			timersub(&next_sendtime, &time_waited, &tv);
		else
			timersub(&timeout, &time_waited, &tv);
		if (tv.tv_sec < 0 || tv.tv_usec < 0)
			tv.tv_sec = tv.tv_usec = 0;
		TIMEVAL_TO_TIMESPEC(&tv, &ts);

		n = rump_sys_pollts(&cu->cu_pfdp, 1, &ts, maskp);
		if (n == 1) {
			/* We have some data now */
			do {
				recvlen = recvfrom(cu->cu_fd, cu->cu_inbuf,
				    cu->cu_recvsz, 0, NULL, NULL);
			} while (recvlen < 0 && errno == EINTR);
			
			if (recvlen < 0 && errno != EWOULDBLOCK) {
				cu->cu_error.re_errno = errno;
				cu->cu_error.re_status = RPC_CANTRECV;
				goto out;
			}
			if (recvlen >= sizeof(uint32_t) &&
			    (*((uint32_t *)(void *)(cu->cu_inbuf)) == 
				*((uint32_t *)(void *)(cu->cu_outbuf)))) {
				/* We now assume we have the proper reply. */
				break;
			}	       
		}
		if (n == -1) {
			cu->cu_error.re_errno = errno;
			cu->cu_error.re_status = RPC_CANTRECV;
			goto out;
		}

		gettimeofday(&tv, NULL);
		timersub(&tv, &starttime, &time_waited);

		/* Check for timeout. */
		if (timercmp(&time_waited, &timeout, >)) {
			cu->cu_error.re_status = RPC_TIMEDOUT;
			goto out;
		}

		/* Retransmit if necessary. */
		if (timercmp(&time_waited, &next_sendtime, >)) {
			/* update retransmit_time */
			if (retransmit_time.tv_sec < RPC_MAX_BACKOFF)
				timeradd(&retransmit_time, &retransmit_time,
				    &retransmit_time);
			timeradd(&next_sendtime, &retransmit_time,
			    &next_sendtime);
			goto send_again;
		}
	}

	/*
	 * now decode and validate the response
	 */

	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
	/* XDR_DESTROY(&reply_xdrs);	save a few cycles on noop destroy */
	if (ok) {
		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
			(reply_msg.acpted_rply.ar_stat == SUCCESS))
			cu->cu_error.re_status = RPC_SUCCESS;
		else
			_seterr_reply(&reply_msg, &(cu->cu_error));

		if (cu->cu_error.re_status == RPC_SUCCESS) {
			if (! AUTH_VALIDATE(cl->cl_auth,
					    &reply_msg.acpted_rply.ar_verf)) {
				cu->cu_error.re_status = RPC_AUTHERROR;
				cu->cu_error.re_why = AUTH_INVALIDRESP;
			}
			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
				xdrs->x_op = XDR_FREE;
				(void) xdr_opaque_auth(xdrs,
					&(reply_msg.acpted_rply.ar_verf));
			}
		}		/* end successful completion */
		/*
		 * If unsuccesful AND error is an authentication error
		 * then refresh credentials and try again, else break
		 */
		else if (cu->cu_error.re_status == RPC_AUTHERROR)
			/* maybe our credentials need to be refreshed ... */
			if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
				nrefreshes--;
				goto call_again;
			}
		/* end of unsuccessful completion */
	}	/* end of valid reply message */
	else {
		cu->cu_error.re_status = RPC_CANTDECODERES;

	}
out:
	release_fd_lock(cu->cu_fd, mask);
	return (cu->cu_error.re_status);
}

static void
clnt_dg_geterr(cl, errp)
	CLIENT *cl;
	struct rpc_err *errp;
{
	struct cu_data *cu;

	_DIAGASSERT(cl != NULL);
	_DIAGASSERT(errp != NULL);

	cu = (struct cu_data *)cl->cl_private;
	*errp = cu->cu_error;
}

static bool_t
clnt_dg_freeres(cl, xdr_res, res_ptr)
	CLIENT *cl;
	xdrproc_t xdr_res;
	caddr_t res_ptr;
{
	struct cu_data *cu;
	XDR *xdrs;
	bool_t dummy;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;

	_DIAGASSERT(cl != NULL);
	cu = (struct cu_data *)cl->cl_private;
	xdrs = &(cu->cu_outxdrs);

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
	while (dg_fd_locks[cu->cu_fd])
		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
	xdrs->x_op = XDR_FREE;
	dummy = (*xdr_res)(xdrs, res_ptr);
	mutex_unlock(&clnt_fd_lock);
	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
	cond_signal(&dg_cv[cu->cu_fd]);
	return (dummy);
}

/*ARGSUSED*/
static void
clnt_dg_abort(h)
	CLIENT *h;
{
}

static bool_t
clnt_dg_control(cl, request, info)
	CLIENT *cl;
	u_int request;
	char *info;
{
	struct cu_data *cu;
	struct netbuf *addr;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;

	_DIAGASSERT(cl != NULL);
	/* info is handled below */

	cu = (struct cu_data *)cl->cl_private;

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
	while (dg_fd_locks[cu->cu_fd])
		cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
	dg_fd_locks[cu->cu_fd] = __rpc_lock_value;
	mutex_unlock(&clnt_fd_lock);
	switch (request) {
	case CLSET_FD_CLOSE:
		cu->cu_closeit = TRUE;
		release_fd_lock(cu->cu_fd, mask);
		return (TRUE);
	case CLSET_FD_NCLOSE:
		cu->cu_closeit = FALSE;
		release_fd_lock(cu->cu_fd, mask);
		return (TRUE);
	}

	/* for other requests which use info */
	if (info == NULL) {
		release_fd_lock(cu->cu_fd, mask);
		return (FALSE);
	}
	switch (request) {
	case CLSET_TIMEOUT:
		if (time_not_ok((struct timeval *)(void *)info)) {
			release_fd_lock(cu->cu_fd, mask);
			return (FALSE);
		}
		cu->cu_total = *(struct timeval *)(void *)info;
		break;
	case CLGET_TIMEOUT:
		*(struct timeval *)(void *)info = cu->cu_total;
		break;
	case CLGET_SERVER_ADDR:		/* Give him the fd address */
		/* Now obsolete. Only for backward compatibility */
		(void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen);
		break;
	case CLSET_RETRY_TIMEOUT:
		if (time_not_ok((struct timeval *)(void *)info)) {
			release_fd_lock(cu->cu_fd, mask);
			return (FALSE);
		}
		cu->cu_wait = *(struct timeval *)(void *)info;
		break;
	case CLGET_RETRY_TIMEOUT:
		*(struct timeval *)(void *)info = cu->cu_wait;
		break;
	case CLGET_FD:
		*(int *)(void *)info = cu->cu_fd;
		break;
	case CLGET_SVC_ADDR:
		addr = (struct netbuf *)(void *)info;
		addr->buf = &cu->cu_raddr;
		addr->len = cu->cu_rlen;
		addr->maxlen = sizeof cu->cu_raddr;
		break;
	case CLSET_SVC_ADDR:		/* set to new address */
		addr = (struct netbuf *)(void *)info;
		if (addr->len < sizeof cu->cu_raddr) {
			release_fd_lock(cu->cu_fd, mask);
			return (FALSE);
		}
		(void) memcpy(&cu->cu_raddr, addr->buf, (size_t)addr->len);
		cu->cu_rlen = addr->len;
		break;
	case CLGET_XID:
		/*
		 * use the knowledge that xid is the
		 * first element in the call structure *.
		 * This will get the xid of the PREVIOUS call
		 */
		*(u_int32_t *)(void *)info =
		    ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
		break;

	case CLSET_XID:
		/* This will set the xid of the NEXT call */
		*(u_int32_t *)(void *)cu->cu_outbuf =
		    htonl(*(u_int32_t *)(void *)info - 1);
		/* decrement by 1 as clnt_dg_call() increments once */
		break;

	case CLGET_VERS:
		/*
		 * This RELIES on the information that, in the call body,
		 * the version number field is the fifth field from the
		 * begining of the RPC header. MUST be changed if the
		 * call_struct is changed
		 */
		*(u_int32_t *)(void *)info =
		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
		    4 * BYTES_PER_XDR_UNIT));
		break;

	case CLSET_VERS:
		*(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
			= htonl(*(u_int32_t *)(void *)info);
		break;

	case CLGET_PROG:
		/*
		 * This RELIES on the information that, in the call body,
		 * the program number field is the fourth field from the
		 * begining of the RPC header. MUST be changed if the
		 * call_struct is changed
		 */
		*(u_int32_t *)(void *)info =
		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
		    3 * BYTES_PER_XDR_UNIT));
		break;

	case CLSET_PROG:
		*(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
			= htonl(*(u_int32_t *)(void *)info);
		break;

	default:
		release_fd_lock(cu->cu_fd, mask);
		return (FALSE);
	}
	release_fd_lock(cu->cu_fd, mask);
	return (TRUE);
}

static void
clnt_dg_destroy(cl)
	CLIENT *cl;
{
	struct cu_data *cu;
	int cu_fd;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;

	_DIAGASSERT(cl != NULL);

	cu = (struct cu_data *)cl->cl_private;
	cu_fd = cu->cu_fd;

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
	while (dg_fd_locks[cu_fd])
		cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
	if (cu->cu_closeit)
		(void) rump_sys_close(cu_fd);
	XDR_DESTROY(&(cu->cu_outxdrs));
	mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
	if (cl->cl_netid && cl->cl_netid[0])
		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
	if (cl->cl_tp && cl->cl_tp[0])
		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
	mem_free(cl, sizeof (CLIENT));
	mutex_unlock(&clnt_fd_lock);
	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
	cond_signal(&dg_cv[cu_fd]);
}

static struct clnt_ops *
clnt_dg_ops()
{
	static struct clnt_ops ops;
#ifdef _REENTRANT
	extern mutex_t	ops_lock;
	sigset_t mask;
#endif
	sigset_t newmask;

/* VARIABLES PROTECTED BY ops_lock: ops */

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&ops_lock);
	if (ops.cl_call == NULL) {
		ops.cl_call = clnt_dg_call;
		ops.cl_abort = clnt_dg_abort;
		ops.cl_geterr = clnt_dg_geterr;
		ops.cl_freeres = clnt_dg_freeres;
		ops.cl_destroy = clnt_dg_destroy;
		ops.cl_control = clnt_dg_control;
	}
	mutex_unlock(&ops_lock);
	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
	return (&ops);
}

/*
 * Make sure that the time is not garbage.  -1 value is allowed.
 */
static bool_t
time_not_ok(t)
	struct timeval *t;
{

	_DIAGASSERT(t != NULL);

	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
		t->tv_usec < -1 || t->tv_usec > 1000000);
}

File Added: src/tests/fs/common/nfsrpc/Attic/clnt_generic.c
/*	$NetBSD: clnt_generic.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
 */

/* #ident	"@(#)clnt_generic.c	1.20	94/05/03 SMI" */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)clnt_generic.c 1.32 89/03/16 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: clnt_generic.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <rpc/rpc.h>
#include <rpc/nettype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "rpc_internal.h"

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

/*
 * Generic client creation with version checking the value of
 * vers_out is set to the highest server supported value
 * vers_low <= vers_out <= vers_high  AND an error results
 * if this can not be done.
 */
CLIENT *
clnt_create_vers(hostname, prog, vers_out, vers_low, vers_high, nettype)
	const char *hostname;
	rpcprog_t prog;
	rpcvers_t *vers_out;
	rpcvers_t vers_low;
	rpcvers_t vers_high;
	const char *nettype;
{
	CLIENT *clnt;
	struct timeval to;
	enum clnt_stat rpc_stat;
	struct rpc_err rpcerr;

	_DIAGASSERT(hostname != NULL);
	_DIAGASSERT(vers_out != NULL);
	/* XXX: nettype appears to support being NULL */

	clnt = clnt_create(hostname, prog, vers_high, nettype);
	if (clnt == NULL) {
		return (NULL);
	}
	to.tv_sec = 10;
	to.tv_usec = 0;
	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
	    NULL, (xdrproc_t) xdr_void, NULL, to);
	if (rpc_stat == RPC_SUCCESS) {
		*vers_out = vers_high;
		return (clnt);
	}
	if (rpc_stat == RPC_PROGVERSMISMATCH) {
		unsigned long minvers, maxvers;

		clnt_geterr(clnt, &rpcerr);
		minvers = rpcerr.re_vers.low;
		maxvers = rpcerr.re_vers.high;
		if (maxvers < vers_high)
			vers_high = (rpcvers_t)maxvers;
		if (minvers > vers_low)
			vers_low = (rpcvers_t)minvers;
		if (vers_low > vers_high) {
			goto error;
		}
		CLNT_CONTROL(clnt, CLSET_VERS, (char *)(void *)&vers_high);
		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
		    NULL, (xdrproc_t) xdr_void, NULL, to);
		if (rpc_stat == RPC_SUCCESS) {
			*vers_out = vers_high;
			return (clnt);
		}
	}
	clnt_geterr(clnt, &rpcerr);

error:
	rpc_createerr.cf_stat = rpc_stat;
	rpc_createerr.cf_error = rpcerr;
	clnt_destroy(clnt);
	return (NULL);
}

/*
 * Top level client creation routine.
 * Generic client creation: takes (servers name, program-number, nettype) and
 * returns client handle. Default options are set, which the user can
 * change using the rpc equivalent of ioctl()'s.
 *
 * It tries for all the netids in that particular class of netid until
 * it succeeds.
 * XXX The error message in the case of failure will be the one
 * pertaining to the last create error.
 *
 * It calls clnt_tp_create();
 */
CLIENT *
clnt_create(hostname, prog, vers, nettype)
	const char *hostname;				/* server name */
	rpcprog_t prog;				/* program number */
	rpcvers_t vers;				/* version number */
	const char *nettype;				/* net type */
{
	struct netconfig *nconf;
	CLIENT *clnt = NULL;
	void *handle;
	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
	struct rpc_err	save_cf_error;

	_DIAGASSERT(hostname != NULL);
	/* XXX: nettype appears to support being NULL */

	if ((handle = __rpc_setconf(nettype)) == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (NULL);
	}
	rpc_createerr.cf_stat = RPC_SUCCESS;
	while (clnt == NULL) {
		if ((nconf = __rpc_getconf(handle)) == NULL) {
			if (rpc_createerr.cf_stat == RPC_SUCCESS)
				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			break;
		}
#ifdef CLNT_DEBUG
		printf("trying netid %s\n", nconf->nc_netid);
#endif
		clnt = clnt_tp_create(hostname, prog, vers, nconf);
		if (clnt)
			break;
		else
			/*
			 *	Since we didn't get a name-to-address
			 *	translation failure here, we remember
			 *	this particular error.  The object of
			 *	this is to enable us to return to the
			 *	caller a more-specific error than the
			 *	unhelpful ``Name to address translation
			 *	failed'' which might well occur if we
			 *	merely returned the last error (because
			 *	the local loopbacks are typically the
			 *	last ones in /etc/netconfig and the most
			 *	likely to be unable to translate a host
			 *	name).
			 */
			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE) {
				save_cf_stat = rpc_createerr.cf_stat;
				save_cf_error = rpc_createerr.cf_error;
			}
	}

	/*
	 *	Attempt to return an error more specific than ``Name to address
	 *	translation failed''
	 */
	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE) &&
		(save_cf_stat != RPC_SUCCESS)) {
		rpc_createerr.cf_stat = save_cf_stat;
		rpc_createerr.cf_error = save_cf_error;
	}
	__rpc_endconf(handle);
	return (clnt);
}

/*
 * Generic client creation: takes (servers name, program-number, netconf) and
 * returns client handle. Default options are set, which the user can
 * change using the rpc equivalent of ioctl()'s : clnt_control()
 * It finds out the server address from rpcbind and calls clnt_tli_create()
 */
CLIENT *
clnt_tp_create(hostname, prog, vers, nconf)
	const char *hostname;			/* server name */
	rpcprog_t prog;				/* program number */
	rpcvers_t vers;				/* version number */
	const struct netconfig *nconf;		/* net config struct */
{
	struct netbuf *svcaddr;			/* servers address */
	CLIENT *cl = NULL;			/* client handle */

	_DIAGASSERT(hostname != NULL);
	/* nconf is handled below */

	if (nconf == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (NULL);
	}

	/*
	 * Get the address of the server
	 */
	if ((svcaddr = __rpcb_findaddr(prog, vers, nconf, hostname,
		&cl)) == NULL) {
		/* appropriate error number is set by rpcbind libraries */
		return (NULL);
	}
	if (cl == NULL) {
		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
					prog, vers, 0, 0);
	} else {
		/* Reuse the CLIENT handle and change the appropriate fields */
		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
			if (cl->cl_netid == NULL) {
				cl->cl_netid = strdup(nconf->nc_netid);
				if (cl->cl_netid == NULL)
					goto out;
			}
			if (cl->cl_tp == NULL) {
				cl->cl_tp = strdup(nconf->nc_device);
				if (cl->cl_tp == NULL)
					goto out;
			}
			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
		} else {
			CLNT_DESTROY(cl);
			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
					prog, vers, 0, 0);
		}
	}
	free(svcaddr->buf);
	free(svcaddr);
	return (cl);
out:
	clnt_destroy(cl);
	return NULL;
}

/*
 * Generic client creation:  returns client handle.
 * Default options are set, which the user can
 * change using the rpc equivalent of ioctl()'s : clnt_control().
 * If fd is RPC_ANYFD, it will be opened using nconf.
 * It will be bound if not so.
 * If sizes are 0; appropriate defaults will be chosen.
 */
CLIENT *
clnt_tli_create(fd, nconf, svcaddr, prog, vers, sendsz, recvsz)
	int fd;				/* fd */
	const struct netconfig *nconf;	/* netconfig structure */
	const struct netbuf *svcaddr;	/* servers address */
	rpcprog_t prog;			/* program number */
	rpcvers_t vers;			/* version number */
	u_int sendsz;			/* send size */
	u_int recvsz;			/* recv size */
{
	CLIENT *cl;			/* client handle */
	bool_t madefd = FALSE;		/* whether fd opened here */
	long servtype;
	struct __rpc_sockinfo si;

	/* nconf is handled below */
	_DIAGASSERT(svcaddr != NULL);

	if (fd == RPC_ANYFD) {
		if (nconf == NULL) {
			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			return (NULL);
		}

		fd = __rpc_nconf2fd(nconf);

		if (fd == -1)
			goto err;

		madefd = TRUE;
		servtype = nconf->nc_semantics;
		if (!__rpc_fd2sockinfo(fd, &si))
			goto err;

		bindresvport(fd, NULL);
	} else {
		if (!__rpc_fd2sockinfo(fd, &si))
			goto err;
		servtype = __rpc_socktype2seman(si.si_socktype);
		if (servtype == -1) {
			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			return NULL;
		}

	}

	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;	/* XXX */
		goto err1;
	}

	switch (servtype) {
	case NC_TPI_COTS_ORD:
		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
		if (!nconf || !cl)
			break;
		__rpc_setnodelay(fd, &si);
		break;
	case NC_TPI_CLTS:
		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
		break;
	default:
		goto err;
	}

	if (cl == NULL)
		goto err1; /* borrow errors from clnt_dg/vc creates */
	if (nconf) {
		cl->cl_netid = strdup(nconf->nc_netid);
		if (cl->cl_netid == NULL)
			goto err0;
		cl->cl_tp = strdup(nconf->nc_device);
		if (cl->cl_tp == NULL)
			goto err0;
	} else {
		cl->cl_netid = __UNCONST("");
		cl->cl_tp = __UNCONST("");
	}
	if (madefd) {
		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
	};

	return (cl);

err0:
	clnt_destroy(cl);
err:
	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
	rpc_createerr.cf_error.re_errno = errno;
err1:	if (madefd)
		(void) rump_sys_close(fd);
	return (NULL);
}

File Added: src/tests/fs/common/nfsrpc/Attic/clnt_vc.c
/*	$NetBSD: clnt_vc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char *sccsid = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
static char *sccsid = "@(#)clnt_tcp.c	2.2 88/08/01 4.0 RPCSRC";
static char sccsid[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: clnt_vc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif
 
/*
 * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
 *
 * Copyright (C) 1984, Sun Microsystems, Inc.
 *
 * TCP based RPC supports 'batched calls'.
 * A sequence of calls may be batched-up in a send buffer.  The rpc call
 * return immediately to the client even though the call was not necessarily
 * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
 * the rpc timeout value is zero (see clnt.h, rpc).
 *
 * Clients should NOT casually batch calls that in fact return results; that is,
 * the server side should be aware that a call is batched and not produce any
 * return message.  Batched calls that produce many result messages can
 * deadlock (netlock) the client and the server....
 *
 * Now go hang yourself.
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/poll.h>
#include <sys/socket.h>

#include <assert.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#include <rpc/rpc.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#include "rpc_internal.h"

#define MCALL_MSG_SIZE 24

static enum clnt_stat clnt_vc_call __P((CLIENT *, rpcproc_t, xdrproc_t,
    const char *, xdrproc_t, caddr_t, struct timeval));
static void clnt_vc_geterr __P((CLIENT *, struct rpc_err *));
static bool_t clnt_vc_freeres __P((CLIENT *, xdrproc_t, caddr_t));
static void clnt_vc_abort __P((CLIENT *));
static bool_t clnt_vc_control __P((CLIENT *, u_int, char *));
static void clnt_vc_destroy __P((CLIENT *));
static struct clnt_ops *clnt_vc_ops __P((void));
static bool_t time_not_ok __P((struct timeval *));
static int read_vc __P((caddr_t, caddr_t, int));
static int write_vc __P((caddr_t, caddr_t, int));

struct ct_data {
	int		ct_fd;
	bool_t		ct_closeit;
	struct timeval	ct_wait;
	bool_t          ct_waitset;       /* wait set by clnt_control? */
	struct netbuf	ct_addr; 
	struct rpc_err	ct_error;
	union {
		char	ct_mcallc[MCALL_MSG_SIZE];	/* marshalled callmsg */
		u_int32_t ct_mcalli;
	} ct_u;
	u_int		ct_mpos;			/* pos after marshal */
	XDR		ct_xdrs;
};

/*
 *      This machinery implements per-fd locks for MT-safety.  It is not
 *      sufficient to do per-CLIENT handle locks for MT-safety because a
 *      user may create more than one CLIENT handle with the same fd behind
 *      it.  Therfore, we allocate an array of flags (vc_fd_locks), protected
 *      by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
 *      similarly protected.  Vc_fd_lock[fd] == 1 => a call is activte on some
 *      CLIENT handle created for that fd.
 *      The current implementation holds locks across the entire RPC and reply.
 *      Yes, this is silly, and as soon as this code is proven to work, this
 *      should be the first thing fixed.  One step at a time.
 */
#ifdef _REENTRANT
static int      *vc_fd_locks;
extern int __isthreaded;
#define __rpc_lock_value __isthreaded;
extern mutex_t  clnt_fd_lock;
static cond_t   *vc_cv;
#define release_fd_lock(fd, mask) {             \
	mutex_lock(&clnt_fd_lock);      \
	vc_fd_locks[fd] = 0;            \
	mutex_unlock(&clnt_fd_lock);    \
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);        \
	cond_signal(&vc_cv[fd]);        \
}
#else
#define release_fd_lock(fd,mask)
#define __rpc_lock_value 0
#endif


/*
 * Create a client handle for a connection.
 * Default options are set, which the user can change using clnt_control()'s.
 * The rpc/vc package does buffering similar to stdio, so the client
 * must pick send and receive buffer sizes, 0 => use the default.
 * NB: fd is copied into a private area.
 * NB: The rpch->cl_auth is set null authentication. Caller may wish to
 * set this something more useful.
 *
 * fd should be an open socket
 */
CLIENT *
clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
	int fd;
	const struct netbuf *raddr;
	rpcprog_t prog;
	rpcvers_t vers;
	u_int sendsz;
	u_int recvsz;
{
	CLIENT *h;
	struct ct_data *ct = NULL;
	struct rpc_msg call_msg;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;
	struct sockaddr_storage ss;
	socklen_t slen;
	struct __rpc_sockinfo si;

	_DIAGASSERT(raddr != NULL);

	h  = mem_alloc(sizeof(*h));
	if (h == NULL) {
		warnx("clnt_vc_create: out of memory");
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
		rpc_createerr.cf_error.re_errno = errno;
		goto fooy;
	}
	ct = mem_alloc(sizeof(*ct));
	if (ct == NULL) {
		warnx("clnt_vc_create: out of memory");
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
		rpc_createerr.cf_error.re_errno = errno;
		goto fooy;
	}


	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
#ifdef _REENTRANT
	mutex_lock(&clnt_fd_lock);
	if (vc_fd_locks == NULL) {
		size_t cv_allocsz, fd_allocsz;
		int dtbsize = __rpc_dtbsize();

		fd_allocsz = dtbsize * sizeof (int);
		vc_fd_locks = mem_alloc(fd_allocsz);
		if (vc_fd_locks == NULL) {
			mutex_unlock(&clnt_fd_lock);
			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
			goto fooy;
		} else
			memset(vc_fd_locks, '\0', fd_allocsz);

		assert(vc_cv == NULL);
		cv_allocsz = dtbsize * sizeof (cond_t);
		vc_cv = mem_alloc(cv_allocsz);
		if (vc_cv == NULL) {
			mem_free(vc_fd_locks, fd_allocsz);
			vc_fd_locks = NULL;
			mutex_unlock(&clnt_fd_lock);
			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
			goto fooy;
		} else {
			int i;

			for (i = 0; i < dtbsize; i++)
				cond_init(&vc_cv[i], 0, (void *) 0);
		}
	} else
		assert(vc_cv != NULL);
#endif

	/*
	 * XXX - fvdl connecting while holding a mutex?
	 */
	slen = sizeof ss;
	if (getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
		if (errno != ENOTCONN) {
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
			rpc_createerr.cf_error.re_errno = errno;
			mutex_unlock(&clnt_fd_lock);
			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
			goto fooy;
		}
		if (connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
			rpc_createerr.cf_error.re_errno = errno;
			mutex_unlock(&clnt_fd_lock);
			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
			goto fooy;
		}
	}
	mutex_unlock(&clnt_fd_lock);
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
	if (!__rpc_fd2sockinfo(fd, &si))
		goto fooy;

	ct->ct_closeit = FALSE;

	/*
	 * Set up private data struct
	 */
	ct->ct_fd = fd;
	ct->ct_wait.tv_usec = 0;
	ct->ct_waitset = FALSE;
	ct->ct_addr.buf = malloc((size_t)raddr->maxlen);
	if (ct->ct_addr.buf == NULL)
		goto fooy;
	memcpy(ct->ct_addr.buf, &raddr->buf, (size_t)raddr->len);
	ct->ct_addr.len = raddr->maxlen;
	ct->ct_addr.maxlen = raddr->maxlen;

	/*
	 * Initialize call message
	 */
	call_msg.rm_xid = __RPC_GETXID();
	call_msg.rm_direction = CALL;
	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
	call_msg.rm_call.cb_prog = (u_int32_t)prog;
	call_msg.rm_call.cb_vers = (u_int32_t)vers;

	/*
	 * pre-serialize the static part of the call msg and stash it away
	 */
	xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
	    XDR_ENCODE);
	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
		if (ct->ct_closeit) {
			(void)rump_sys_close(fd);
		}
		goto fooy;
	}
	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
	XDR_DESTROY(&(ct->ct_xdrs));

	/*
	 * Create a client handle which uses xdrrec for serialization
	 * and authnone for authentication.
	 */
	h->cl_ops = clnt_vc_ops();
	h->cl_private = ct;
	h->cl_auth = authnone_create();
	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
	    h->cl_private, read_vc, write_vc);
	return (h);

fooy:
	/*
	 * Something goofed, free stuff and barf
	 */
	if (ct)
		mem_free(ct, sizeof(struct ct_data));
	if (h)
		mem_free(h, sizeof(CLIENT));
	return (NULL);
}

static enum clnt_stat
clnt_vc_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
	CLIENT *h;
	rpcproc_t proc;
	xdrproc_t xdr_args;
	const char *args_ptr;
	xdrproc_t xdr_results;
	caddr_t results_ptr;
	struct timeval timeout;
{
	struct ct_data *ct;
	XDR *xdrs;
	struct rpc_msg reply_msg;
	u_int32_t x_id;
	u_int32_t *msg_x_id;
	bool_t shipnow;
	int refreshes = 2;
#ifdef _REENTRANT
	sigset_t mask, newmask;
#endif

	_DIAGASSERT(h != NULL);

	ct = (struct ct_data *) h->cl_private;

#ifdef _REENTRANT
	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
	while (vc_fd_locks[ct->ct_fd])
		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
	vc_fd_locks[ct->ct_fd] = __rpc_lock_value;
	mutex_unlock(&clnt_fd_lock);
#endif

	xdrs = &(ct->ct_xdrs);
	msg_x_id = &ct->ct_u.ct_mcalli;

	if (!ct->ct_waitset) {
		if (time_not_ok(&timeout) == FALSE)
		ct->ct_wait = timeout;
	}

	shipnow =
	    (xdr_results == NULL && timeout.tv_sec == 0
	    && timeout.tv_usec == 0) ? FALSE : TRUE;

call_again:
	xdrs->x_op = XDR_ENCODE;
	ct->ct_error.re_status = RPC_SUCCESS;
	x_id = ntohl(--(*msg_x_id));
	if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
	    (! (*xdr_args)(xdrs, __UNCONST(args_ptr)))) {
		if (ct->ct_error.re_status == RPC_SUCCESS)
			ct->ct_error.re_status = RPC_CANTENCODEARGS;
		(void)xdrrec_endofrecord(xdrs, TRUE);
		release_fd_lock(ct->ct_fd, mask);
		return (ct->ct_error.re_status);
	}
	if (! xdrrec_endofrecord(xdrs, shipnow)) {
		release_fd_lock(ct->ct_fd, mask);
		return (ct->ct_error.re_status = RPC_CANTSEND);
	}
	if (! shipnow) {
		release_fd_lock(ct->ct_fd, mask);
		return (RPC_SUCCESS);
	}
	/*
	 * Hack to provide rpc-based message passing
	 */
	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
		release_fd_lock(ct->ct_fd, mask);
		return(ct->ct_error.re_status = RPC_TIMEDOUT);
	}


	/*
	 * Keep receiving until we get a valid transaction id
	 */
	xdrs->x_op = XDR_DECODE;
	for (;;) {
		reply_msg.acpted_rply.ar_verf = _null_auth;
		reply_msg.acpted_rply.ar_results.where = NULL;
		reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
		if (! xdrrec_skiprecord(xdrs)) {
			release_fd_lock(ct->ct_fd, mask);
			return (ct->ct_error.re_status);
		}
		/* now decode and validate the response header */
		if (! xdr_replymsg(xdrs, &reply_msg)) {
			if (ct->ct_error.re_status == RPC_SUCCESS)
				continue;
			release_fd_lock(ct->ct_fd, mask);
			return (ct->ct_error.re_status);
		}
		if (reply_msg.rm_xid == x_id)
			break;
	}

	/*
	 * process header
	 */
	_seterr_reply(&reply_msg, &(ct->ct_error));
	if (ct->ct_error.re_status == RPC_SUCCESS) {
		if (! AUTH_VALIDATE(h->cl_auth,
		    &reply_msg.acpted_rply.ar_verf)) {
			ct->ct_error.re_status = RPC_AUTHERROR;
			ct->ct_error.re_why = AUTH_INVALIDRESP;
		} else if (! (*xdr_results)(xdrs, results_ptr)) {
			if (ct->ct_error.re_status == RPC_SUCCESS)
				ct->ct_error.re_status = RPC_CANTDECODERES;
		}
		/* free verifier ... */
		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
			xdrs->x_op = XDR_FREE;
			(void)xdr_opaque_auth(xdrs,
			    &(reply_msg.acpted_rply.ar_verf));
		}
	}  /* end successful completion */
	else {
		/* maybe our credentials need to be refreshed ... */
		if (refreshes-- && AUTH_REFRESH(h->cl_auth))
			goto call_again;
	}  /* end of unsuccessful completion */
	release_fd_lock(ct->ct_fd, mask);
	return (ct->ct_error.re_status);
}

static void
clnt_vc_geterr(h, errp)
	CLIENT *h;
	struct rpc_err *errp;
{
	struct ct_data *ct;

	_DIAGASSERT(h != NULL);
	_DIAGASSERT(errp != NULL);

	ct = (struct ct_data *) h->cl_private;
	*errp = ct->ct_error;
}

static bool_t
clnt_vc_freeres(cl, xdr_res, res_ptr)
	CLIENT *cl;
	xdrproc_t xdr_res;
	caddr_t res_ptr;
{
	struct ct_data *ct;
	XDR *xdrs;
	bool_t dummy;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;

	_DIAGASSERT(cl != NULL);

	ct = (struct ct_data *)cl->cl_private;
	xdrs = &(ct->ct_xdrs);

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
#ifdef _REENTRANT
	while (vc_fd_locks[ct->ct_fd])
		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
#endif

	xdrs->x_op = XDR_FREE;
	dummy = (*xdr_res)(xdrs, res_ptr);
	mutex_unlock(&clnt_fd_lock);
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
	cond_signal(&vc_cv[ct->ct_fd]);

	return dummy;
}

/*ARGSUSED*/
static void
clnt_vc_abort(cl)
	CLIENT *cl;
{
}

static bool_t
clnt_vc_control(cl, request, info)
	CLIENT *cl;
	u_int request;
	char *info;
{
	struct ct_data *ct;
	void *infop = info;
#ifdef _REENTRANT
	sigset_t mask;
#endif
	sigset_t newmask;

	_DIAGASSERT(cl != NULL);

	ct = (struct ct_data *)cl->cl_private;

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
#ifdef _REENTRANT
	while (vc_fd_locks[ct->ct_fd])
		cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
	vc_fd_locks[ct->ct_fd] = __rpc_lock_value;
#endif
	mutex_unlock(&clnt_fd_lock);

	switch (request) {
	case CLSET_FD_CLOSE:
		ct->ct_closeit = TRUE;
		release_fd_lock(ct->ct_fd, mask);
		return (TRUE);
	case CLSET_FD_NCLOSE:
		ct->ct_closeit = FALSE;
		release_fd_lock(ct->ct_fd, mask);
		return (TRUE);
	default:
		break;
	}

	/* for other requests which use info */
	if (info == NULL) {
		release_fd_lock(ct->ct_fd, mask);
		return (FALSE);
	}
	switch (request) {
	case CLSET_TIMEOUT:
		if (time_not_ok((struct timeval *)(void *)info)) {
			release_fd_lock(ct->ct_fd, mask);
			return (FALSE);
		}
		ct->ct_wait = *(struct timeval *)infop;
		ct->ct_waitset = TRUE;
		break;
	case CLGET_TIMEOUT:
		*(struct timeval *)infop = ct->ct_wait;
		break;
	case CLGET_SERVER_ADDR:
		(void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
		break;
	case CLGET_FD:
		*(int *)(void *)info = ct->ct_fd;
		break;
	case CLGET_SVC_ADDR:
		/* The caller should not free this memory area */
		*(struct netbuf *)(void *)info = ct->ct_addr;
		break;
	case CLSET_SVC_ADDR:		/* set to new address */
		release_fd_lock(ct->ct_fd, mask);
		return (FALSE);
	case CLGET_XID:
		/*
		 * use the knowledge that xid is the
		 * first element in the call structure
		 * This will get the xid of the PREVIOUS call
		 */
		*(u_int32_t *)(void *)info =
		    ntohl(*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli);
		break;
	case CLSET_XID:
		/* This will set the xid of the NEXT call */
		*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli =
		    htonl(*((u_int32_t *)(void *)info) + 1);
		/* increment by 1 as clnt_vc_call() decrements once */
		break;
	case CLGET_VERS:
		/*
		 * This RELIES on the information that, in the call body,
		 * the version number field is the fifth field from the
		 * begining of the RPC header. MUST be changed if the
		 * call_struct is changed
		 */
		*(u_int32_t *)(void *)info =
		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
		    4 * BYTES_PER_XDR_UNIT));
		break;

	case CLSET_VERS:
		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
		    4 * BYTES_PER_XDR_UNIT) =
		    htonl(*(u_int32_t *)(void *)info);
		break;

	case CLGET_PROG:
		/*
		 * This RELIES on the information that, in the call body,
		 * the program number field is the fourth field from the
		 * begining of the RPC header. MUST be changed if the
		 * call_struct is changed
		 */
		*(u_int32_t *)(void *)info =
		    ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
		    3 * BYTES_PER_XDR_UNIT));
		break;

	case CLSET_PROG:
		*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
		    3 * BYTES_PER_XDR_UNIT) =
		    htonl(*(u_int32_t *)(void *)info);
		break;

	default:
		release_fd_lock(ct->ct_fd, mask);
		return (FALSE);
	}
	release_fd_lock(ct->ct_fd, mask);
	return (TRUE);
}


static void
clnt_vc_destroy(cl)
	CLIENT *cl;
{
	struct ct_data *ct;
#ifdef _REENTRANT
	int ct_fd;
	sigset_t mask;
#endif
	sigset_t newmask;

	_DIAGASSERT(cl != NULL);

	ct = (struct ct_data *) cl->cl_private;
	ct_fd = ct->ct_fd;

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&clnt_fd_lock);
#ifdef _REENTRANT
	while (vc_fd_locks[ct_fd])
		cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
#endif
	if (ct->ct_closeit && ct->ct_fd != -1) {
		(void)rump_sys_close(ct->ct_fd);
	}
	XDR_DESTROY(&(ct->ct_xdrs));
	if (ct->ct_addr.buf)
		free(ct->ct_addr.buf);
	mem_free(ct, sizeof(struct ct_data));
	mem_free(cl, sizeof(CLIENT));
	mutex_unlock(&clnt_fd_lock);
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);

	cond_signal(&vc_cv[ct_fd]);
}

/*
 * Interface between xdr serializer and tcp connection.
 * Behaves like the system calls, read & write, but keeps some error state
 * around for the rpc level.
 */
static int
read_vc(ctp, buf, len)
	caddr_t ctp;
	caddr_t buf;
	int len;
{
	struct ct_data *ct = (struct ct_data *)(void *)ctp;
	struct pollfd fd;
	struct timespec ts;

	if (len == 0)
		return (0);

	TIMEVAL_TO_TIMESPEC(&ct->ct_wait, &ts);
	fd.fd = ct->ct_fd;
	fd.events = POLLIN;
	for (;;) {
		switch (rump_sys_pollts(&fd, 1, &ts, NULL)) {
		case 0:
			ct->ct_error.re_status = RPC_TIMEDOUT;
			return (-1);

		case -1:
			if (errno == EINTR)
				continue;
			ct->ct_error.re_status = RPC_CANTRECV;
			ct->ct_error.re_errno = errno;
			return (-1);
		}
		break;
	}
	switch (len = rump_sys_read(ct->ct_fd, buf, (size_t)len)) {

	case 0:
		/* premature eof */
		ct->ct_error.re_errno = ECONNRESET;
		ct->ct_error.re_status = RPC_CANTRECV;
		len = -1;  /* it's really an error */
		break;

	case -1:
		ct->ct_error.re_errno = errno;
		ct->ct_error.re_status = RPC_CANTRECV;
		break;
	}
	return (len);
}

static int
write_vc(ctp, buf, len)
	caddr_t ctp;
	caddr_t buf;
	int len;
{
	struct ct_data *ct = (struct ct_data *)(void *)ctp;
	int i, cnt;

	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
		if ((i = rump_sys_write(ct->ct_fd, buf, (size_t)cnt)) == -1) {
			ct->ct_error.re_errno = errno;
			ct->ct_error.re_status = RPC_CANTSEND;
			return (-1);
		}
	}
	return (len);
}

static struct clnt_ops *
clnt_vc_ops()
{
	static struct clnt_ops ops;
#ifdef _REENTRANT
	extern mutex_t  ops_lock;
	sigset_t mask;
#endif
	sigset_t newmask;

	/* VARIABLES PROTECTED BY ops_lock: ops */

	sigfillset(&newmask);
	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
	mutex_lock(&ops_lock);
	if (ops.cl_call == NULL) {
		ops.cl_call = clnt_vc_call;
		ops.cl_abort = clnt_vc_abort;
		ops.cl_geterr = clnt_vc_geterr;
		ops.cl_freeres = clnt_vc_freeres;
		ops.cl_destroy = clnt_vc_destroy;
		ops.cl_control = clnt_vc_control;
	}
	mutex_unlock(&ops_lock);
	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
	return (&ops);
}

/*
 * Make sure that the time is not garbage.   -1 value is disallowed.
 * Note this is different from time_not_ok in clnt_dg.c
 */
static bool_t
time_not_ok(t)
	struct timeval *t;
{

	_DIAGASSERT(t != NULL);

	return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
		t->tv_usec <= -1 || t->tv_usec > 1000000);
}

File Added: src/tests/fs/common/nfsrpc/Attic/namespace.h
/*	$NetBSD: namespace.h,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*-
 * Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _NAMESPACE_H_
#define _NAMESPACE_H_

#include <sys/cdefs.h>

/* rpc locks */
#define authdes_lock		__rpc_authdes_lock
#define authnone_lock		__rpc_authnone_lock
#define authsvc_lock		__rpc_authsvc_lock
#define clnt_fd_lock		__rpc_clnt_fd_lock
#define clntraw_lock		__rpc_clntraw_lock
#define dname_lock		__rpc_dname_lock
#define dupreq_lock		__rpc_dupreq_lock
#define keyserv_lock		__rpc_keyserv_lock
#define libnsl_trace_lock	__rpc_libnsl_trace_lock
#define loopnconf_lock		__rpc_loopnconf_lock
#define ops_lock		__rpc_ops_lock
#define portnum_lock		__rpc_portnum_lock
#define proglst_lock		__rpc_proglst_lock
#define rpcbaddr_cache_lock	__rpc_rpcbaddr_cache_lock
#define rpcsoc_lock		__rpc_rpcsoc_lock
#define svc_fd_lock		__rpc_svc_fd_lock
#define svc_lock		__rpc_svc_lock
#define svcraw_lock		__rpc_svcraw_lock
#define xprtlist_lock		__rpc_xprtlist_lock

#endif /* _NAMESPACE_H_ */

File Added: src/tests/fs/common/nfsrpc/Attic/reentrant.h
/*	$NetBSD: reentrant.h,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*-
 * Copyright (c) 1997, 1998, 2003 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by J.T. Conklin, by Nathan J. Williams, and by Jason R. Thorpe.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Requirements:
 * 
 * 1. The thread safe mechanism should be lightweight so the library can
 *    be used by non-threaded applications without unreasonable overhead.
 * 
 * 2. There should be no dependency on a thread engine for non-threaded
 *    applications.
 * 
 * 3. There should be no dependency on any particular thread engine.
 * 
 * 4. The library should be able to be compiled without support for thread
 *    safety.
 * 
 * 
 * Rationale:
 * 
 * One approach for thread safety is to provide discrete versions of the
 * library: one thread safe, the other not.  The disadvantage of this is
 * that libc is rather large, and two copies of a library which are 99%+
 * identical is not an efficent use of resources.
 * 
 * Another approach is to provide a single thread safe library.  However,
 * it should not add significant run time or code size overhead to non-
 * threaded applications.
 * 
 * Since the NetBSD C library is used in other projects, it should be
 * easy to replace the mutual exclusion primitives with ones provided by
 * another system.  Similarly, it should also be easy to remove all
 * support for thread safety completely if the target environment does
 * not support threads.
 * 
 * 
 * Implementation Details:
 * 
 * The thread primitives used by the library (mutex_t, mutex_lock, etc.)
 * are macros which expand to the cooresponding primitives provided by
 * the thread engine or to nothing.  The latter is used so that code is
 * not unreasonably cluttered with #ifdefs when all thread safe support
 * is removed.
 * 
 * The thread macros can be directly mapped to the mutex primitives from
 * pthreads, however it should be reasonably easy to wrap another mutex
 * implementation so it presents a similar interface.
 * 
 * The thread functions operate by dispatching to symbols which are, by
 * default, weak-aliased to no-op functions in thread-stub/thread-stub.c
 * (some uses of thread operations are conditional on __isthreaded, but
 * not all of them are).
 *
 * When the thread library is linked in, it provides strong-alias versions
 * of those symbols which dispatch to its own real thread operations.
 *
 */

#ifdef _REENTRANT

/*
 * Abstract thread interface for thread-safe libraries.  These routines
 * will use stubs in libc if the application is not linked against the
 * pthread library, and the real function in the pthread library if it
 * is.
 */

#include <pthread.h>
#include <signal.h>

#define	mutex_t			pthread_mutex_t
#define	MUTEX_INITIALIZER	PTHREAD_MUTEX_INITIALIZER

#define	mutexattr_t		pthread_mutexattr_t

#define	MUTEX_TYPE_NORMAL	PTHREAD_MUTEX_NORMAL
#define	MUTEX_TYPE_ERRORCHECK	PTHREAD_MUTEX_ERRORCHECK
#define	MUTEX_TYPE_RECURSIVE	PTHREAD_MUTEX_RECURSIVE

#define	cond_t			pthread_cond_t
#define	COND_INITIALIZER	PTHREAD_COND_INITIALIZER

#define	condattr_t		pthread_condattr_t

#define	rwlock_t		pthread_rwlock_t
#define	RWLOCK_INITIALIZER	PTHREAD_RWLOCK_INITIALIZER

#define	rwlockattr_t		pthread_rwlockattr_t

#define	thread_key_t		pthread_key_t

#define	thr_t			pthread_t

#define	thrattr_t		pthread_attr_t

#define	once_t			pthread_once_t
#define	ONCE_INITIALIZER	PTHREAD_ONCE_INIT

#ifndef __LIBC_THREAD_STUBS

__BEGIN_DECLS
int	__libc_mutex_init(mutex_t *, const mutexattr_t *);
int	__libc_mutex_lock(mutex_t *);
int	__libc_mutex_trylock(mutex_t *);
int	__libc_mutex_unlock(mutex_t *);
int	__libc_mutex_destroy(mutex_t *);

int	__libc_mutexattr_init(mutexattr_t *);
int	__libc_mutexattr_settype(mutexattr_t *, int);
int	__libc_mutexattr_destroy(mutexattr_t *);
__END_DECLS

#define	mutex_init(m, a)	__libc_mutex_init((m), (a))
#define	mutex_lock(m)		__libc_mutex_lock((m))
#define	mutex_trylock(m)	__libc_mutex_trylock((m))
#define	mutex_unlock(m)		__libc_mutex_unlock((m))
#define	mutex_destroy(m)	__libc_mutex_destroy((m))

#define	mutexattr_init(ma)	__libc_mutexattr_init((ma))
#define	mutexattr_settype(ma, t) __libc_mutexattr_settype((ma), (t))
#define	mutexattr_destroy(ma)	__libc_mutexattr_destroy((ma))

__BEGIN_DECLS
int	__libc_cond_init(cond_t *, const condattr_t *);
int	__libc_cond_signal(cond_t *);
int	__libc_cond_broadcast(cond_t *);
int	__libc_cond_wait(cond_t *, mutex_t *);
int	__libc_cond_timedwait(cond_t *, mutex_t *, const struct timespec *);
int	__libc_cond_destroy(cond_t *);
__END_DECLS

#define	cond_init(c, t, a)     	__libc_cond_init((c), (a))
#define	cond_signal(c)		__libc_cond_signal((c))
#define	cond_broadcast(c)	__libc_cond_broadcast((c))
#define	cond_wait(c, m)		__libc_cond_wait((c), (m))
#define	cond_timedwait(c, m, t)	__libc_cond_timedwait((c), (m), (t))
#define	cond_destroy(c)		__libc_cond_destroy((c))

__BEGIN_DECLS
int	__libc_rwlock_init(rwlock_t *, const rwlockattr_t *);
int	__libc_rwlock_rdlock(rwlock_t *);
int	__libc_rwlock_wrlock(rwlock_t *);
int	__libc_rwlock_tryrdlock(rwlock_t *);
int	__libc_rwlock_trywrlock(rwlock_t *);
int	__libc_rwlock_unlock(rwlock_t *);
int	__libc_rwlock_destroy(rwlock_t *);
__END_DECLS

#define	rwlock_init(l, a)	__libc_rwlock_init((l), (a))
#define	rwlock_rdlock(l)	__libc_rwlock_rdlock((l))
#define	rwlock_wrlock(l)	__libc_rwlock_wrlock((l))
#define	rwlock_tryrdlock(l)	__libc_rwlock_tryrdlock((l))
#define	rwlock_trywrlock(l)	__libc_rwlock_trywrlock((l))
#define	rwlock_unlock(l)	__libc_rwlock_unlock((l))
#define	rwlock_destroy(l)	__libc_rwlock_destroy((l))

__BEGIN_DECLS
int	__libc_thr_keycreate(thread_key_t *, void (*)(void *));
int	__libc_thr_setspecific(thread_key_t, const void *);
void	*__libc_thr_getspecific(thread_key_t);
int	__libc_thr_keydelete(thread_key_t);
__END_DECLS

#define	thr_keycreate(k, d)	__libc_thr_keycreate((k), (d))
#define	thr_setspecific(k, p)	__libc_thr_setspecific((k), (p))
#define	thr_getspecific(k)	__libc_thr_getspecific((k))
#define	thr_keydelete(k)	__libc_thr_keydelete((k))

__BEGIN_DECLS
int	__libc_thr_once(once_t *, void (*)(void));
int	__libc_thr_sigsetmask(int, const sigset_t *, sigset_t *);
thr_t	__libc_thr_self(void);
int	__libc_thr_yield(void);
void	__libc_thr_create(thr_t *, const thrattr_t *,
	    void *(*)(void *), void *);
void	__libc_thr_exit(void *) __attribute__((__noreturn__));
int	*__libc_thr_errno(void);
int	__libc_thr_setcancelstate(int, int *);
unsigned int	__libc_thr_curcpu(void);

extern int __isthreaded;
__END_DECLS

#define	thr_once(o, f)		__libc_thr_once((o), (f))
#define	thr_sigsetmask(f, n, o)	__libc_thr_sigsetmask((f), (n), (o))
#define	thr_self()		__libc_thr_self()
#define	thr_yield()		__libc_thr_yield()
#define	thr_create(tp, ta, f, a) __libc_thr_create((tp), (ta), (f), (a))
#define	thr_exit(v)		__libc_thr_exit((v))
#define	thr_errno()		__libc_thr_errno()
#define	thr_enabled()		(__isthreaded)
#define thr_setcancelstate(n, o) __libc_thr_setcancelstate((n),(o))
#define thr_curcpu()		__libc_thr_curcpu()
#endif /* __LIBC_THREAD_STUBS */

#define	FLOCKFILE(fp)		__flockfile_internal(fp, 1)
#define	FUNLOCKFILE(fp)		__funlockfile_internal(fp, 1)

#else /* _REENTRANT */

#define	mutex_init(m, a)
#define	mutex_lock(m)
#define	mutex_trylock(m)
#define	mutex_unlock(m)
#define	mutex_destroy(m)

#define	cond_init(c, t, a)
#define	cond_signal(c)
#define	cond_broadcast(c)
#define	cond_wait(c, m)
#define	cond_timedwait(c, m, t)
#define	cond_destroy(c)

#define	rwlock_init(l, a)
#define	rwlock_rdlock(l)
#define	rwlock_wrlock(l)
#define	rwlock_tryrdlock(l)
#define	rwlock_trywrlock(l)
#define	rwlock_unlock(l)
#define	rwlock_destroy(l)

#define	thr_keycreate(k, d)
#define	thr_setspecific(k, p)
#define	thr_getspecific(k)
#define	thr_keydelete(k)

#define	thr_once(o, f)
#define	thr_sigsetmask(f, n, o)
#define	thr_self()
#define	thr_errno()
#define	thr_curcpu()		((unsigned int)0)

#define	FLOCKFILE(fp)		
#define	FUNLOCKFILE(fp)		

#endif /* _REENTRANT */

File Added: src/tests/fs/common/nfsrpc/Attic/rpc_generic.c
/*	$NetBSD: rpc_generic.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
 */

/* #pragma ident	"@(#)rpc_generic.c	1.17	94/04/24 SMI" */

/*
 * rpc_generic.c, Miscl routines for RPC.
 *
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: rpc_generic.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <netdb.h>
#include <netconfig.h>
#include <malloc.h>
#include <string.h>
#include <syslog.h>
#include <rpc/nettype.h>
#include "rpc_internal.h"

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

struct handle {
	NCONF_HANDLE *nhandle;
	int nflag;		/* Whether NETPATH or NETCONFIG */
	int nettype;
};

static const struct _rpcnettype {
	const char *name;
	const int type;
} _rpctypelist[] = {
	{ "netpath", _RPC_NETPATH },
	{ "visible", _RPC_VISIBLE },
	{ "circuit_v", _RPC_CIRCUIT_V },
	{ "datagram_v", _RPC_DATAGRAM_V },
	{ "circuit_n", _RPC_CIRCUIT_N },
	{ "datagram_n", _RPC_DATAGRAM_N },
	{ "tcp", _RPC_TCP },
	{ "udp", _RPC_UDP },
	{ 0, _RPC_NONE }
};

struct netid_af {
	const char	*netid;
	int		af;
	int		protocol;
};

static const struct netid_af na_cvt[] = {
	{ "udp",  AF_INET,  IPPROTO_UDP },
	{ "tcp",  AF_INET,  IPPROTO_TCP },
#ifdef INET6
	{ "udp6", AF_INET6, IPPROTO_UDP },
	{ "tcp6", AF_INET6, IPPROTO_TCP },
#endif
	{ "local", AF_LOCAL, 0 }
};

#if 0
static char *strlocase __P((char *));
#endif
static int getnettype __P((const char *));

/*
 * Cache the result of getrlimit(), so we don't have to do an
 * expensive call every time.
 */
int
__rpc_dtbsize()
{
	static int tbsize;
	struct rlimit rl;

	if (tbsize) {
		return (tbsize);
	}
	if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
		return (tbsize = (int)rl.rlim_max);
	}
	/*
	 * Something wrong.  I'll try to save face by returning a
	 * pessimistic number.
	 */
	return (32);
}


/*
 * Find the appropriate buffer size
 */
u_int
/*ARGSUSED*/
__rpc_get_t_size(af, proto, size)
	int af, proto;
	int size;	/* Size requested */
{
	int maxsize, defsize;

	maxsize = 256 * 1024;	/* XXX */
	switch (proto) {
	case IPPROTO_TCP:
		defsize = 64 * 1024;	/* XXX */
		break;
	case IPPROTO_UDP:
		defsize = UDPMSGSIZE;
		break;
	default:
		defsize = RPC_MAXDATASIZE;
		break;
	}
	if (size == 0)
		return defsize;

	/* Check whether the value is within the upper max limit */
	return (size > maxsize ? (u_int)maxsize : (u_int)size);
}

/*
 * Find the appropriate address buffer size
 */
u_int
__rpc_get_a_size(af)
	int af;
{
	switch (af) {
	case AF_INET:
		return sizeof (struct sockaddr_in);
#ifdef INET6
	case AF_INET6:
		return sizeof (struct sockaddr_in6);
#endif
	case AF_LOCAL:
		return sizeof (struct sockaddr_un);
	default:
		break;
	}
	return ((u_int)RPC_MAXADDRSIZE);
}

#if 0
static char *
strlocase(p)
	char *p;
{
	char *t = p;

	_DIAGASSERT(p != NULL);

	for (; *p; p++)
		if (isupper(*p))
			*p = tolower(*p);
	return (t);
}
#endif

/*
 * Returns the type of the network as defined in <rpc/nettype.h>
 * If nettype is NULL, it defaults to NETPATH.
 */
static int
getnettype(nettype)
	const char *nettype;
{
	int i;

	if ((nettype == NULL) || (nettype[0] == 0)) {
		return (_RPC_NETPATH);	/* Default */
	}

#if 0
	nettype = strlocase(nettype);
#endif
	for (i = 0; _rpctypelist[i].name; i++)
		if (strcasecmp(nettype, _rpctypelist[i].name) == 0) {
			return (_rpctypelist[i].type);
		}
	return (_rpctypelist[i].type);
}

/*
 * For the given nettype (tcp or udp only), return the first structure found.
 * This should be freed by calling freenetconfigent()
 */

#ifdef _REENTRANT
static thread_key_t tcp_key, udp_key;
static once_t __rpc_getconfigp_once = ONCE_INITIALIZER;

static void
__rpc_getconfigp_setup(void)
{

	thr_keycreate(&tcp_key, free);
	thr_keycreate(&udp_key, free);
}
#endif

struct netconfig *
__rpc_getconfip(nettype)
	const char *nettype;
{
	char *netid;
	char *netid_tcp = NULL;
	char *netid_udp = NULL;
	static char *netid_tcp_main;
	static char *netid_udp_main;
	struct netconfig *dummy;
#ifdef _REENTRANT
	extern int __isthreaded;

	if (__isthreaded == 0) {
		netid_udp = netid_udp_main;
		netid_tcp = netid_tcp_main;
	} else {
		thr_once(&__rpc_getconfigp_once, __rpc_getconfigp_setup);
		netid_tcp = thr_getspecific(tcp_key);
		netid_udp = thr_getspecific(udp_key);
	}
#else
	netid_udp = netid_udp_main;
	netid_tcp = netid_tcp_main;
#endif

	_DIAGASSERT(nettype != NULL);

	if (!netid_udp && !netid_tcp) {
		struct netconfig *nconf;
		void *confighandle;

		if (!(confighandle = setnetconfig())) {
			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
			return (NULL);
		}
		while ((nconf = getnetconfig(confighandle)) != NULL) {
			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
				if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
					netid_tcp = strdup(nconf->nc_netid);
					if (netid_tcp == NULL)
						return NULL;
#ifdef _REENTRANT
					if (__isthreaded == 0)
						netid_tcp_main = netid_tcp;
					else
						thr_setspecific(tcp_key,
							(void *) netid_tcp);
#else
					netid_tcp_main = netid_tcp;
#endif
				} else
				if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
					netid_udp = strdup(nconf->nc_netid);
					if (netid_udp == NULL)
						return NULL;
#ifdef _REENTRANT
					if (__isthreaded == 0)
						netid_udp_main = netid_udp;
					else
						thr_setspecific(udp_key,
							(void *) netid_udp);
#else
					netid_udp_main = netid_udp;
#endif
				}
			}
		}
		endnetconfig(confighandle);
	}
	if (strcmp(nettype, "udp") == 0)
		netid = netid_udp;
	else if (strcmp(nettype, "tcp") == 0)
		netid = netid_tcp;
	else {
		return (NULL);
	}
	if ((netid == NULL) || (netid[0] == 0)) {
		return (NULL);
	}
	dummy = getnetconfigent(netid);
	return (dummy);
}

/*
 * Returns the type of the nettype, which should then be used with
 * __rpc_getconf().
 */
void *
__rpc_setconf(nettype)
	const char *nettype;
{
	struct handle *handle;

	/* nettype may be NULL; getnettype() supports that */

	handle = malloc(sizeof(*handle));
	if (handle == NULL) {
		return (NULL);
	}
	switch (handle->nettype = getnettype(nettype)) {
	case _RPC_NETPATH:
	case _RPC_CIRCUIT_N:
	case _RPC_DATAGRAM_N:
		if (!(handle->nhandle = setnetpath())) {
			free(handle);
			return (NULL);
		}
		handle->nflag = TRUE;
		break;
	case _RPC_VISIBLE:
	case _RPC_CIRCUIT_V:
	case _RPC_DATAGRAM_V:
	case _RPC_TCP:
	case _RPC_UDP:
		if (!(handle->nhandle = setnetconfig())) {
		        syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
			free(handle);
			return (NULL);
		}
		handle->nflag = FALSE;
		break;
	default:
		free(handle);
		return (NULL);
	}

	return (handle);
}

/*
 * Returns the next netconfig struct for the given "net" type.
 * __rpc_setconf() should have been called previously.
 */
struct netconfig *
__rpc_getconf(vhandle)
	void *vhandle;
{
	struct handle *handle;
	struct netconfig *nconf;

	handle = (struct handle *)vhandle;
	if (handle == NULL) {
		return (NULL);
	}
	for (;;) {
		if (handle->nflag)
			nconf = getnetpath(handle->nhandle);
		else
			nconf = getnetconfig(handle->nhandle);
		if (nconf == NULL)
			break;
		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
			(nconf->nc_semantics != NC_TPI_COTS) &&
			(nconf->nc_semantics != NC_TPI_COTS_ORD))
			continue;
		switch (handle->nettype) {
		case _RPC_VISIBLE:
			if (!(nconf->nc_flag & NC_VISIBLE))
				continue;
			/* FALLTHROUGH */
		case _RPC_NETPATH:	/* Be happy */
			break;
		case _RPC_CIRCUIT_V:
			if (!(nconf->nc_flag & NC_VISIBLE))
				continue;
			/* FALLTHROUGH */
		case _RPC_CIRCUIT_N:
			if ((nconf->nc_semantics != NC_TPI_COTS) &&
				(nconf->nc_semantics != NC_TPI_COTS_ORD))
				continue;
			break;
		case _RPC_DATAGRAM_V:
			if (!(nconf->nc_flag & NC_VISIBLE))
				continue;
			/* FALLTHROUGH */
		case _RPC_DATAGRAM_N:
			if (nconf->nc_semantics != NC_TPI_CLTS)
				continue;
			break;
		case _RPC_TCP:
			if (((nconf->nc_semantics != NC_TPI_COTS) &&
				(nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
				(strcmp(nconf->nc_protofmly, NC_INET)
#ifdef INET6
				 && strcmp(nconf->nc_protofmly, NC_INET6))
#else
				)
#endif
				||
				strcmp(nconf->nc_proto, NC_TCP))
				continue;
			break;
		case _RPC_UDP:
			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
				(strcmp(nconf->nc_protofmly, NC_INET)
#ifdef INET6
				&& strcmp(nconf->nc_protofmly, NC_INET6))
#else
				)
#endif
				||
				strcmp(nconf->nc_proto, NC_UDP))
				continue;
			break;
		}
		break;
	}
	return (nconf);
}

void
__rpc_endconf(vhandle)
	void * vhandle;
{
	struct handle *handle;

	handle = (struct handle *) vhandle;
	if (handle == NULL) {
		return;
	}
	if (handle->nflag) {
		endnetpath(handle->nhandle);
	} else {
		endnetconfig(handle->nhandle);
	}
	free(handle);
}

/*
 * Used to ping the NULL procedure for clnt handle.
 * Returns NULL if fails, else a non-NULL pointer.
 */
void *
rpc_nullproc(clnt)
	CLIENT *clnt;
{
	struct timeval TIMEOUT = {25, 0};

	if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL,
		(xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
		return (NULL);
	}
	return ((void *) clnt);
}

/*
 * Try all possible transports until
 * one succeeds in finding the netconf for the given fd.
 */
struct netconfig *
__rpcgettp(fd)
	int fd;
{
	const char *netid;
	struct __rpc_sockinfo si;

	if (!__rpc_fd2sockinfo(fd, &si))
		return NULL;

	if (!__rpc_sockinfo2netid(&si, &netid))
		return NULL;

	return getnetconfigent(__UNCONST(netid));
}

int
__rpc_fd2sockinfo(int fd, struct __rpc_sockinfo *sip)
{
	socklen_t len;
	int type, proto;
	struct sockaddr_storage ss;

	_DIAGASSERT(sip != NULL);

	len = sizeof ss;
	if (getsockname(fd, (struct sockaddr *)(void *)&ss, &len) < 0)
		return 0;
	sip->si_alen = len;

	len = sizeof type;
	if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0)
		return 0;

	/* XXX */
	if (ss.ss_family != AF_LOCAL) {
		if (type == SOCK_STREAM)
			proto = IPPROTO_TCP;
		else if (type == SOCK_DGRAM)
			proto = IPPROTO_UDP;
		else
			return 0;
	} else
		proto = 0;

	sip->si_af = ss.ss_family;
	sip->si_proto = proto;
	sip->si_socktype = type;

	return 1;
}

/*
 * Linear search, but the number of entries is small.
 */
int
__rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip)
{
	size_t i;

	_DIAGASSERT(nconf != NULL);
	_DIAGASSERT(sip != NULL);

	for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
		if (!strcmp(na_cvt[i].netid, nconf->nc_netid)) {
			sip->si_af = na_cvt[i].af;
			sip->si_proto = na_cvt[i].protocol;
			sip->si_socktype =
			    __rpc_seman2socktype((int)nconf->nc_semantics);
			if (sip->si_socktype == -1)
				return 0;
			sip->si_alen = __rpc_get_a_size(sip->si_af);
			return 1;
		}

	return 0;
}

int
__rpc_nconf2fd(const struct netconfig *nconf)
{
	struct __rpc_sockinfo si;

	_DIAGASSERT(nconf != NULL);

	if (!__rpc_nconf2sockinfo(nconf, &si))
		return 0;

	return socket(si.si_af, si.si_socktype, si.si_proto);
}

int
__rpc_sockinfo2netid(struct __rpc_sockinfo *sip, const char **netid)
{
	size_t i;

	_DIAGASSERT(sip != NULL);
	/* netid may be NULL */

	for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
		if (na_cvt[i].af == sip->si_af &&
		    na_cvt[i].protocol == sip->si_proto) {
			if (netid)
				*netid = na_cvt[i].netid;
			return 1;
		}

	return 0;
}

char *
taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
{
	struct __rpc_sockinfo si;

	_DIAGASSERT(nconf != NULL);
	_DIAGASSERT(nbuf != NULL);

	if (!__rpc_nconf2sockinfo(nconf, &si))
		return NULL;
	return __rpc_taddr2uaddr_af(si.si_af, nbuf);
}

struct netbuf *
uaddr2taddr(const struct netconfig *nconf, const char *uaddr)
{
	struct __rpc_sockinfo si;

	_DIAGASSERT(nconf != NULL);
	_DIAGASSERT(uaddr != NULL);
	
	if (!__rpc_nconf2sockinfo(nconf, &si))
		return NULL;
	return __rpc_uaddr2taddr_af(si.si_af, uaddr);
}

char *
__rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
{
	char *ret;
	struct sockaddr_in *sinp;
	struct sockaddr_un *sun;
	char namebuf[INET_ADDRSTRLEN];
#ifdef INET6
	struct sockaddr_in6 *sin6;
	char namebuf6[INET6_ADDRSTRLEN];
#endif
	u_int16_t port;

	_DIAGASSERT(nbuf != NULL);

	switch (af) {
	case AF_INET:
		sinp = nbuf->buf;
		if (inet_ntop(af, &sinp->sin_addr, namebuf, sizeof namebuf)
		    == NULL)
			return NULL;
		port = ntohs(sinp->sin_port);
		if (asprintf(&ret, "%s.%u.%u", namebuf, ((u_int32_t)port) >> 8,
		    port & 0xff) < 0)
			return NULL;
		break;
#ifdef INET6
	case AF_INET6:
		sin6 = nbuf->buf;
		if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6)
		    == NULL)
			return NULL;
		port = ntohs(sin6->sin6_port);
		if (asprintf(&ret, "%s.%u.%u", namebuf6, ((u_int32_t)port) >> 8,
		    port & 0xff) < 0)
			return NULL;
		break;
#endif
	case AF_LOCAL:
		sun = nbuf->buf;
		sun->sun_path[sizeof(sun->sun_path) - 1] = '\0'; /* safety */
		ret = strdup(sun->sun_path);
		break;
	default:
		return NULL;
	}

	return ret;
}

struct netbuf *
__rpc_uaddr2taddr_af(int af, const char *uaddr)
{
	struct netbuf *ret = NULL;
	char *addrstr, *p;
	unsigned port, portlo, porthi;
	struct sockaddr_in *sinp;
#ifdef INET6
	struct sockaddr_in6 *sin6;
#endif
	struct sockaddr_un *sun;

	_DIAGASSERT(uaddr != NULL);

	addrstr = strdup(uaddr);
	if (addrstr == NULL)
		return NULL;

	/*
	 * AF_LOCAL addresses are expected to be absolute
	 * pathnames, anything else will be AF_INET or AF_INET6.
	 */
	port = 0;
	if (*addrstr != '/') {
		p = strrchr(addrstr, '.');
		if (p == NULL)
			goto out;
		portlo = (unsigned)atoi(p + 1);
		*p = '\0';

		p = strrchr(addrstr, '.');
		if (p == NULL)
			goto out;
		porthi = (unsigned)atoi(p + 1);
		*p = '\0';
		port = (porthi << 8) | portlo;
	}

	ret = malloc(sizeof(*ret));
	if (ret == NULL)
		goto out;
	
	switch (af) {
	case AF_INET:
		sinp = malloc(sizeof(*sinp));
		if (sinp == NULL)
			goto out;
		memset(sinp, 0, sizeof *sinp);
		sinp->sin_family = AF_INET;
		sinp->sin_port = htons(port);
		if (inet_pton(AF_INET, addrstr, &sinp->sin_addr) <= 0) {
			free(sinp);
			free(ret);
			ret = NULL;
			goto out;
		}
		sinp->sin_len = ret->maxlen = ret->len = sizeof *sinp;
		ret->buf = sinp;
		break;
#ifdef INET6
	case AF_INET6:
		sin6 = malloc(sizeof(*sin6));
		if (sin6 == NULL)
			goto out;
		memset(sin6, 0, sizeof *sin6);
		sin6->sin6_family = AF_INET6;
		sin6->sin6_port = htons(port);
		if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) {
			free(sin6);
			free(ret);
			ret = NULL;
			goto out;
		}
		sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
		ret->buf = sin6;
		break;
#endif
	case AF_LOCAL:
		sun = malloc(sizeof(*sun));
		if (sun == NULL)
			goto out;
		memset(sun, 0, sizeof *sun);
		sun->sun_family = AF_LOCAL;
		strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
		ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
		ret->buf = sun;
		break;
	default:
		break;
	}
out:
	free(addrstr);
	return ret;
}

int
__rpc_seman2socktype(int semantics)
{
	switch (semantics) {
	case NC_TPI_CLTS:
		return SOCK_DGRAM;
	case NC_TPI_COTS_ORD:
		return SOCK_STREAM;
	case NC_TPI_RAW:
		return SOCK_RAW;
	default:
		break;
	}

	return -1;
}

int
__rpc_socktype2seman(int socktype)
{
	switch (socktype) {
	case SOCK_DGRAM:
		return NC_TPI_CLTS;
	case SOCK_STREAM:
		return NC_TPI_COTS_ORD;
	case SOCK_RAW:
		return NC_TPI_RAW;
	default:
		break;
	}

	return -1;
}

/*
 * XXXX - IPv6 scope IDs can't be handled in universal addresses.
 * Here, we compare the original server address to that of the RPC
 * service we just received back from a call to rpcbind on the remote
 * machine. If they are both "link local" or "site local", copy
 * the scope id of the server address over to the service address.
 */
/* ARGSUSED */
int
__rpc_fixup_addr(struct netbuf *new, const struct netbuf *svc)
{
#ifdef INET6
	struct sockaddr *sa_new, *sa_svc;
	struct sockaddr_in6 *sin6_new, *sin6_svc;

	_DIAGASSERT(new != NULL);
	_DIAGASSERT(svc != NULL);

	sa_svc = (struct sockaddr *)svc->buf;
	sa_new = (struct sockaddr *)new->buf;

	if (sa_new->sa_family == sa_svc->sa_family &&
	    sa_new->sa_family == AF_INET6) {
		sin6_new = (struct sockaddr_in6 *)new->buf;
		sin6_svc = (struct sockaddr_in6 *)svc->buf;

		if ((IN6_IS_ADDR_LINKLOCAL(&sin6_new->sin6_addr) &&
		     IN6_IS_ADDR_LINKLOCAL(&sin6_svc->sin6_addr)) ||
		    (IN6_IS_ADDR_SITELOCAL(&sin6_new->sin6_addr) &&
		     IN6_IS_ADDR_SITELOCAL(&sin6_svc->sin6_addr))) {
			sin6_new->sin6_scope_id = sin6_svc->sin6_scope_id;
		}
	}
#endif
	return 1;
}

int
__rpc_sockisbound(int fd)
{
	struct sockaddr_storage ss;
	socklen_t slen;

	slen = sizeof (struct sockaddr_storage);
	if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0)
		return 0;

	switch (ss.ss_family) {
		case AF_INET:
			return (((struct sockaddr_in *)
			    (void *)&ss)->sin_port != 0);
#ifdef INET6
		case AF_INET6:
			return (((struct sockaddr_in6 *)
			    (void *)&ss)->sin6_port != 0);
#endif
		case AF_LOCAL:
			/* XXX check this */
			return (((struct sockaddr_un *)
			    (void *)&ss)->sun_path[0] != '\0');
		default:
			break;
	}

	return 0;
}

/*
 * For TCP transport, Host Requirements RFCs mandate
 * Nagle (RFC-896) processing.  But for RPC, Nagle
 * processing adds adds unwanted latency to the last,
 * partial TCP segment of each RPC message. See:
 *   R. W. Scheifler and J. Gettys, The X Window System,
 *   ACM Transactions on Graphics 16:8 (Aug. 1983), pp. 57-69. 
 * So for TCP transport, disable Nagle via TCP_NODELAY.
 * XXX: moral equivalent for non-TCP protocols?
 */
int
__rpc_setnodelay(int fd, const struct __rpc_sockinfo *si)
{
	int one = 1;
	if (si->si_proto != IPPROTO_TCP)
		return 0;
	return setsockopt(fd, si->si_proto, TCP_NODELAY, &one, sizeof(one));
}

File Added: src/tests/fs/common/nfsrpc/Attic/rpc_internal.h
/*	$NetBSD: rpc_internal.h,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*-
 * Copyright (c) 2004 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Frank van der Linden.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Private include file for XDR functions only used internally in libc.
 * These are not exported interfaces.
 */

bool_t __xdrrec_getrec __P((XDR *, enum xprt_stat *, bool_t));
bool_t __xdrrec_setnonblock __P((XDR *, int));
void __xprt_unregister_unlocked __P((SVCXPRT *));
bool_t __svc_clean_idle __P((fd_set *, int, bool_t));

bool_t __xdrrec_getrec __P((XDR *, enum xprt_stat *, bool_t));
bool_t __xdrrec_setnonblock __P((XDR *, int));

u_int __rpc_get_a_size __P((int));
int __rpc_dtbsize __P((void));
struct netconfig * __rpcgettp __P((int));
int  __rpc_get_default_domain __P((char **));

char *__rpc_taddr2uaddr_af __P((int, const struct netbuf *));
struct netbuf *__rpc_uaddr2taddr_af __P((int, const char *));
int __rpc_fixup_addr __P((struct netbuf *, const struct netbuf *));
int __rpc_sockinfo2netid __P((struct __rpc_sockinfo *, const char **));
int __rpc_seman2socktype __P((int));
int __rpc_socktype2seman __P((int));
void *rpc_nullproc __P((CLIENT *));
int __rpc_sockisbound __P((int));

struct netbuf *__rpcb_findaddr __P((rpcprog_t, rpcvers_t,
				    const struct netconfig *,
				    const char *, CLIENT **));
bool_t __rpc_control __P((int,void *));

char *_get_next_token __P((char *, int));

u_int32_t __rpc_getxid __P((void));
#define __RPC_GETXID()	(__rpc_getxid())

extern SVCXPRT **__svc_xports;
extern int __svc_maxrec;

File Added: src/tests/fs/common/nfsrpc/Attic/rpc_soc.c
/*	$NetBSD: rpc_soc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/* #ident	"@(#)rpc_soc.c	1.17	94/04/24 SMI" */

/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
 * In addition, portions of such source code were derived from Berkeley
 * 4.3 BSD under license from the Regents of the University of
 * California.
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)rpc_soc.c 1.41 89/05/02 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: rpc_soc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

#ifdef PORTMAP
/*
 * rpc_soc.c
 *
 * The backward compatibility routines for the earlier implementation
 * of RPC, where the only transports supported were tcp/ip and udp/ip.
 * Based on berkeley socket abstraction, now implemented on the top
 * of TLI/Streams
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <rpc/nettype.h>
#include <netinet/in.h>
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include "rpc_internal.h"

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#ifdef _REENTRANT
extern mutex_t	rpcsoc_lock;
#endif

static CLIENT *clnt_com_create __P((struct sockaddr_in *, rpcprog_t, rpcvers_t,
				    int *, u_int, u_int, const char *));
static SVCXPRT *svc_com_create __P((int, u_int, u_int, const char *));
static bool_t rpc_wrap_bcast __P((char *, struct netbuf *, struct netconfig *));

/*
 * A common clnt create routine
 */
static CLIENT *
clnt_com_create(raddr, prog, vers, sockp, sendsz, recvsz, tp)
	struct sockaddr_in *raddr;
	rpcprog_t prog;
	rpcvers_t vers;
	int *sockp;
	u_int sendsz;
	u_int recvsz;
	const char *tp;
{
	CLIENT *cl;
	int madefd = FALSE;
	int fd;
	struct netconfig *nconf;
	struct netbuf bindaddr;

	_DIAGASSERT(raddr != NULL);
	_DIAGASSERT(sockp != NULL);
	_DIAGASSERT(tp != NULL);

	fd = *sockp;

	mutex_lock(&rpcsoc_lock);
	if ((nconf = __rpc_getconfip(tp)) == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		mutex_unlock(&rpcsoc_lock);
		return (NULL);
	}
	if (fd == RPC_ANYSOCK) {
		fd = __rpc_nconf2fd(nconf);
		if (fd == -1)
			goto syserror;
		madefd = TRUE;
	}

	if (raddr->sin_port == 0) {
		u_int proto;
		u_short sport;

		mutex_unlock(&rpcsoc_lock);	/* pmap_getport is recursive */
		proto = strcmp(tp, "udp") == 0 ? IPPROTO_UDP : IPPROTO_TCP;
		sport = pmap_getport(raddr, (u_long)prog, (u_long)vers,
		    proto);
		if (sport == 0) {
			goto err;
		}
		raddr->sin_port = htons(sport);
		mutex_lock(&rpcsoc_lock);	/* pmap_getport is recursive */
	}

	/* Transform sockaddr_in to netbuf */
	bindaddr.maxlen = bindaddr.len =  sizeof (struct sockaddr_in);
	bindaddr.buf = raddr;

	bindresvport(fd, NULL);
	cl = clnt_tli_create(fd, nconf, &bindaddr, prog, vers,
				sendsz, recvsz);
	if (cl) {
		if (madefd == TRUE) {
			/*
			 * The fd should be closed while destroying the handle.
			 */
			(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
			*sockp = fd;
		}
		(void) freenetconfigent(nconf);
		mutex_unlock(&rpcsoc_lock);
		return (cl);
	}
	goto err;

syserror:
	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
	rpc_createerr.cf_error.re_errno = errno;

err:	if (madefd == TRUE)
		(void) rump_sys_close(fd);
	(void) freenetconfigent(nconf);
	mutex_unlock(&rpcsoc_lock);
	return (NULL);
}

CLIENT *
clntudp_bufcreate(raddr, prog, vers, wait, sockp, sendsz, recvsz)
	struct sockaddr_in *raddr;
	u_long prog;
	u_long vers;
	struct timeval wait;
	int *sockp;
	u_int sendsz;
	u_int recvsz;
{
	CLIENT *cl;

	_DIAGASSERT(raddr != NULL);
	_DIAGASSERT(sockp != NULL);

	cl = clnt_com_create(raddr, (rpcprog_t)prog, (rpcvers_t)vers, sockp,
	    sendsz, recvsz, "udp");
	if (cl == NULL) {
		return (NULL);
	}
	(void) CLNT_CONTROL(cl, CLSET_RETRY_TIMEOUT, (char *)(void *)&wait);
	return (cl);
}

CLIENT *
clntudp_create(raddr, program, version, wait, sockp)
	struct sockaddr_in *raddr;
	u_long program;
	u_long version;
	struct timeval wait;
	int *sockp;
{
	return clntudp_bufcreate(raddr, program, version, wait, sockp,
					UDPMSGSIZE, UDPMSGSIZE);
}

CLIENT *
clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
	struct sockaddr_in *raddr;
	u_long prog;
	u_long vers;
	int *sockp;
	u_int sendsz;
	u_int recvsz;
{
	return clnt_com_create(raddr, (rpcprog_t)prog, (rpcvers_t)vers, sockp,
	    sendsz, recvsz, "tcp");
}

CLIENT *
clntraw_create(prog, vers)
	u_long prog;
	u_long vers;
{
	return clnt_raw_create((rpcprog_t)prog, (rpcvers_t)vers);
}

/*
 * A common server create routine
 */
static SVCXPRT *
svc_com_create(fd, sendsize, recvsize, netid)
	int fd;
	u_int sendsize;
	u_int recvsize;
	const char *netid;
{
	struct netconfig *nconf;
	SVCXPRT *svc;
	int madefd = FALSE;
	int port;
	struct sockaddr_in sccsin;

	_DIAGASSERT(netid != NULL);

	if ((nconf = __rpc_getconfip(netid)) == NULL) {
		(void) syslog(LOG_ERR, "Could not get %s transport", netid);
		return (NULL);
	}
	if (fd == RPC_ANYSOCK) {
		fd = __rpc_nconf2fd(nconf);
		if (fd == -1) {
			(void) freenetconfigent(nconf);
			(void) syslog(LOG_ERR,
			"svc%s_create: could not open connection", netid);
			return (NULL);
		}
		madefd = TRUE;
	}

	memset(&sccsin, 0, sizeof sccsin);
	sccsin.sin_family = AF_INET;
	bindresvport(fd, &sccsin);
	listen(fd, SOMAXCONN);
	svc = svc_tli_create(fd, nconf, NULL, sendsize, recvsize);
	(void) freenetconfigent(nconf);
	if (svc == NULL) {
		if (madefd)
			(void) rump_sys_close(fd);
		return (NULL);
	}
	port = (((struct sockaddr_in *)svc->xp_ltaddr.buf)->sin_port);
	svc->xp_port = ntohs(port);
	return (svc);
}

SVCXPRT *
svctcp_create(fd, sendsize, recvsize)
	int fd;
	u_int sendsize;
	u_int recvsize;
{
	return svc_com_create(fd, sendsize, recvsize, "tcp");
}

SVCXPRT *
svcudp_bufcreate(fd, sendsz, recvsz)
	int fd;
	u_int sendsz, recvsz;
{
	return svc_com_create(fd, sendsz, recvsz, "udp");
}

SVCXPRT *
svcfd_create(fd, sendsize, recvsize)
	int fd;
	u_int sendsize;
	u_int recvsize;
{
	return svc_fd_create(fd, sendsize, recvsize);
}


SVCXPRT *
svcudp_create(fd)
	int fd;
{
	return svc_com_create(fd, UDPMSGSIZE, UDPMSGSIZE, "udp");
}

SVCXPRT *
svcraw_create()
{
	return svc_raw_create();
}

int
get_myaddress(addr)
	struct sockaddr_in *addr;
{

	_DIAGASSERT(addr != NULL);

	memset((void *) addr, 0, sizeof(*addr));
	addr->sin_family = AF_INET;
	addr->sin_port = htons(PMAPPORT);
	addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	return (0);
}

/*
 * For connectionless "udp" transport. Obsoleted by rpc_call().
 */
int
callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
	char *host;
	int prognum, versnum, procnum;
	xdrproc_t inproc, outproc;
	char *in, *out;
{
	return (int)rpc_call(host, (rpcprog_t)prognum, (rpcvers_t)versnum,
	    (rpcproc_t)procnum, inproc, in, outproc, out, "udp");
}

/*
 * For connectionless kind of transport. Obsoleted by rpc_reg()
 */
int
registerrpc(prognum, versnum, procnum, progname, inproc, outproc)
	int prognum, versnum, procnum;
	char *(*progname) __P((char [UDPMSGSIZE]));
	xdrproc_t inproc, outproc;
{
	return rpc_reg((rpcprog_t)prognum, (rpcvers_t)versnum,
	    (rpcproc_t)procnum, progname, inproc, outproc, __UNCONST("udp"));
}

/*
 * All the following clnt_broadcast stuff is convulated; it supports
 * the earlier calling style of the callback function
 */
#ifdef _REENTRANT
static thread_key_t	clnt_broadcast_key;
#endif
static resultproc_t	clnt_broadcast_result_main;

/*
 * Need to translate the netbuf address into sockaddr_in address.
 * Dont care about netid here.
 */
/* ARGSUSED */
static bool_t
rpc_wrap_bcast(resultp, addr, nconf)
	char *resultp;		/* results of the call */
	struct netbuf *addr;	/* address of the guy who responded */
	struct netconfig *nconf; /* Netconf of the transport */
{
	resultproc_t clnt_broadcast_result;
#ifdef _REENTRANT
	extern int __isthreaded;
#endif

	_DIAGASSERT(resultp != NULL);
	_DIAGASSERT(addr != NULL);
	_DIAGASSERT(nconf != NULL);

	if (strcmp(nconf->nc_netid, "udp"))
		return (FALSE);
#ifdef _REENTRANT
	if (__isthreaded == 0)
		clnt_broadcast_result = clnt_broadcast_result_main;
	else
		clnt_broadcast_result = thr_getspecific(clnt_broadcast_key);
#else
	clnt_broadcast_result = clnt_broadcast_result_main;
#endif
	return (*clnt_broadcast_result)(resultp,
				(struct sockaddr_in *)addr->buf);
}

#ifdef _REENTRANT
static once_t clnt_broadcast_once = ONCE_INITIALIZER;

static void
clnt_broadcast_setup(void)
{

	thr_keycreate(&clnt_broadcast_key, free);
}
#endif

/*
 * Broadcasts on UDP transport. Obsoleted by rpc_broadcast().
 */
enum clnt_stat
clnt_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, eachresult)
	u_long		prog;		/* program number */
	u_long		vers;		/* version number */
	u_long		proc;		/* procedure number */
	xdrproc_t	xargs;		/* xdr routine for args */
	caddr_t		argsp;		/* pointer to args */
	xdrproc_t	xresults;	/* xdr routine for results */
	caddr_t		resultsp;	/* pointer to results */
	resultproc_t	eachresult;	/* call with each result obtained */
{
#ifdef _REENTRANT
	extern int __isthreaded;

	if (__isthreaded == 0)
		clnt_broadcast_result_main = eachresult;
	else {
		thr_once(&clnt_broadcast_once, clnt_broadcast_setup);
		thr_setspecific(clnt_broadcast_key, (void *) eachresult);
	}
#else
	clnt_broadcast_result_main = eachresult;
#endif
	return rpc_broadcast((rpcprog_t)prog, (rpcvers_t)vers,
	    (rpcproc_t)proc, xargs, argsp, xresults, resultsp,
	    (resultproc_t) rpc_wrap_bcast, "udp");
}

#endif /* PORTMAP */

File Added: src/tests/fs/common/nfsrpc/Attic/rpcb_clnt.c
/*	$NetBSD: rpcb_clnt.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
 */

/* #ident	"@(#)rpcb_clnt.c	1.27	94/04/24 SMI" */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: rpcb_clnt.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * rpcb_clnt.c
 * interface to rpcbind rpc service.
 *
 * Copyright (C) 1988, Sun Microsystems, Inc.
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <rpc/rpc.h>
#include <rpc/rpcb_prot.h>
#include <rpc/nettype.h>
#include <netconfig.h>
#ifdef PORTMAP
#include <netinet/in.h>		/* FOR IPPROTO_TCP/UDP definitions */
#include <rpc/pmap_prot.h>
#endif
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#include "rpc_internal.h"

static struct timeval tottimeout = { 60, 0 };
static const struct timeval rmttimeout = { 3, 0 };

static const char nullstring[] = "\000";

#define	CACHESIZE 6

struct address_cache {
	char *ac_host;
	char *ac_netid;
	char *ac_uaddr;
	struct netbuf *ac_taddr;
	struct address_cache *ac_next;
};

static struct address_cache *front;
static int cachesize;

#define	CLCR_GET_RPCB_TIMEOUT	1
#define	CLCR_SET_RPCB_TIMEOUT	2


extern int __rpc_lowvers;

static struct address_cache *check_cache __P((const char *, const char *));
static void delete_cache __P((struct netbuf *));
static void add_cache __P((const char *, const char *, struct netbuf *,
			   char *));
static CLIENT *getclnthandle __P((const char *, const struct netconfig *,
				  char **));
static CLIENT *local_rpcb __P((void));
static struct netbuf *got_entry __P((rpcb_entry_list_ptr,
				     const struct netconfig *));

/*
 * This routine adjusts the timeout used for calls to the remote rpcbind.
 * Also, this routine can be used to set the use of portmapper version 2
 * only when doing rpc_broadcasts
 * These are private routines that may not be provided in future releases.
 */
bool_t
__rpc_control(request, info)
	int	request;
	void	*info;
{

	_DIAGASSERT(info != NULL);

	switch (request) {
	case CLCR_GET_RPCB_TIMEOUT:
		*(struct timeval *)info = tottimeout;
		break;
	case CLCR_SET_RPCB_TIMEOUT:
		tottimeout = *(struct timeval *)info;
		break;
	case CLCR_SET_LOWVERS:
		__rpc_lowvers = *(int *)info;
		break;
	case CLCR_GET_LOWVERS:
		*(int *)info = __rpc_lowvers;
		break;
	default:
		return (FALSE);
	}
	return (TRUE);
}

/*
 *	It might seem that a reader/writer lock would be more reasonable here.
 *	However because getclnthandle(), the only user of the cache functions,
 *	may do a delete_cache() operation if a check_cache() fails to return an
 *	address useful to clnt_tli_create(), we may as well use a mutex.
 */
/*
 * As it turns out, if the cache lock is *not* a reader/writer lock, we will
 * block all clnt_create's if we are trying to connect to a host that's down,
 * since the lock will be held all during that time.
 */
#ifdef _REENTRANT
extern rwlock_t	rpcbaddr_cache_lock;
#endif

/*
 * The routines check_cache(), add_cache(), delete_cache() manage the
 * cache of rpcbind addresses for (host, netid).
 */

static struct address_cache *
check_cache(host, netid)
	const char *host, *netid;
{
	struct address_cache *cptr;

	_DIAGASSERT(host != NULL);
	_DIAGASSERT(netid != NULL);

	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */

	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
		if (!strcmp(cptr->ac_host, host) &&
		    !strcmp(cptr->ac_netid, netid)) {
#ifdef ND_DEBUG
			fprintf(stderr, "Found cache entry for %s: %s\n",
				host, netid);
#endif
			return (cptr);
		}
	}
	return NULL;
}

static void
delete_cache(addr)
	struct netbuf *addr;
{
	struct address_cache *cptr, *prevptr = NULL;

	_DIAGASSERT(addr != NULL);

	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
			free(cptr->ac_host);
			free(cptr->ac_netid);
			free(cptr->ac_taddr->buf);
			free(cptr->ac_taddr);
			if (cptr->ac_uaddr)
				free(cptr->ac_uaddr);
			if (prevptr)
				prevptr->ac_next = cptr->ac_next;
			else
				front = cptr->ac_next;
			free(cptr);
			cachesize--;
			break;
		}
		prevptr = cptr;
	}
}

static void
add_cache(host, netid, taddr, uaddr)
	const char *host, *netid;
	char *uaddr;
	struct netbuf *taddr;
{
	struct address_cache  *ad_cache, *cptr, *prevptr;

	_DIAGASSERT(host != NULL);
	_DIAGASSERT(netid != NULL);
	/* uaddr may be NULL */
	/* taddr may be NULL ??? */

	ad_cache = malloc(sizeof(*ad_cache));
	if (!ad_cache) {
		return;
	}
	ad_cache->ac_host = strdup(host);
	ad_cache->ac_netid = strdup(netid);
	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
	ad_cache->ac_taddr = malloc(sizeof(*ad_cache->ac_taddr));
	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
		(uaddr && !ad_cache->ac_uaddr)) {
		goto out;
	}
	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
	ad_cache->ac_taddr->buf = malloc(taddr->len);
	if (ad_cache->ac_taddr->buf == NULL) {
out:
		if (ad_cache->ac_host)
			free(ad_cache->ac_host);
		if (ad_cache->ac_netid)
			free(ad_cache->ac_netid);
		if (ad_cache->ac_uaddr)
			free(ad_cache->ac_uaddr);
		if (ad_cache->ac_taddr)
			free(ad_cache->ac_taddr);
		free(ad_cache);
		return;
	}
	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
#ifdef ND_DEBUG
	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
#endif

/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */

	rwlock_wrlock(&rpcbaddr_cache_lock);
	if (cachesize < CACHESIZE) {
		ad_cache->ac_next = front;
		front = ad_cache;
		cachesize++;
	} else {
		/* Free the last entry */
		cptr = front;
		prevptr = NULL;
		while (cptr->ac_next) {
			prevptr = cptr;
			cptr = cptr->ac_next;
		}

#ifdef ND_DEBUG
		fprintf(stderr, "Deleted from cache: %s : %s\n",
			cptr->ac_host, cptr->ac_netid);
#endif
		free(cptr->ac_host);
		free(cptr->ac_netid);
		free(cptr->ac_taddr->buf);
		free(cptr->ac_taddr);
		if (cptr->ac_uaddr)
			free(cptr->ac_uaddr);

		if (prevptr) {
			prevptr->ac_next = NULL;
			ad_cache->ac_next = front;
			front = ad_cache;
		} else {
			front = ad_cache;
			ad_cache->ac_next = NULL;
		}
		free(cptr);
	}
	rwlock_unlock(&rpcbaddr_cache_lock);
}

/*
 * This routine will return a client handle that is connected to the
 * rpcbind. Returns NULL on error and free's everything.
 */
static CLIENT *
getclnthandle(host, nconf, targaddr)
	const char *host;
	const struct netconfig *nconf;
	char **targaddr;
{
	CLIENT *client;
	struct netbuf *addr, taddr;
	struct netbuf addr_to_delete;
	struct __rpc_sockinfo si;
	struct addrinfo hints, *res, *tres;
	struct address_cache *ad_cache;
	char *tmpaddr;

	_DIAGASSERT(host != NULL);
	_DIAGASSERT(nconf != NULL);
	/* targaddr may be NULL */

/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */

	/* Get the address of the rpcbind.  Check cache first */
	client = NULL;
	addr_to_delete.len = 0;
	addr_to_delete.buf = NULL;
	rwlock_rdlock(&rpcbaddr_cache_lock);
	ad_cache = check_cache(host, nconf->nc_netid);
	if (ad_cache != NULL) {
		addr = ad_cache->ac_taddr;
		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
		if (client != NULL) {
			if (targaddr)
				*targaddr = ad_cache->ac_uaddr;
			rwlock_unlock(&rpcbaddr_cache_lock);
			return (client);
		}
		addr_to_delete.len = addr->len;
		addr_to_delete.buf = malloc(addr->len);
		if (addr_to_delete.buf == NULL) {
			addr_to_delete.len = 0;
		} else {
			memcpy(addr_to_delete.buf, addr->buf, addr->len);
		}
	}
	rwlock_unlock(&rpcbaddr_cache_lock);
	if (addr_to_delete.len != 0) {
		/*
		 * Assume this may be due to cache data being
		 *  outdated
		 */
		rwlock_wrlock(&rpcbaddr_cache_lock);
		delete_cache(&addr_to_delete);
		rwlock_unlock(&rpcbaddr_cache_lock);
		free(addr_to_delete.buf);
	}
	if (!__rpc_nconf2sockinfo(nconf, &si)) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return NULL;
	}

	memset(&hints, 0, sizeof hints);
	hints.ai_family = si.si_af;
	hints.ai_socktype = si.si_socktype;
	hints.ai_protocol = si.si_proto;

#ifdef CLNT_DEBUG
	printf("trying netid %s family %d proto %d socktype %d\n",
	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
#endif

	if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
		return NULL;
	}

	for (tres = res; tres != NULL; tres = tres->ai_next) {
		taddr.buf = tres->ai_addr;
		taddr.len = taddr.maxlen = tres->ai_addrlen;

#ifdef ND_DEBUG
		{
			char *ua;

			ua = taddr2uaddr(nconf, &taddr);
			fprintf(stderr, "Got it [%s]\n", ua);
			free(ua);
		}
#endif

#ifdef ND_DEBUG
		{
			int i;

			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
				taddr.len, taddr.maxlen);
			fprintf(stderr, "\tAddress is ");
			for (i = 0; i < taddr.len; i++)
				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
			fprintf(stderr, "\n");
		}
#endif
		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
#ifdef ND_DEBUG
		if (! client) {
			clnt_pcreateerror("rpcbind clnt interface");
		}
#endif

		if (client) {
			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
			if (targaddr)
				*targaddr = tmpaddr;
			break;
		}
	}
	freeaddrinfo(res);
	return (client);
}

/* XXX */
#define IN4_LOCALHOST_STRING	"127.0.0.1"
#define IN6_LOCALHOST_STRING	"::1"

/*
 * This routine will return a client handle that is connected to the local
 * rpcbind. Returns NULL on error and free's everything.
 */
static CLIENT *
local_rpcb()
{
	CLIENT *client;
	static struct netconfig *loopnconf;
	static const char *hostname;
#ifdef _REENTRANT
	extern mutex_t loopnconf_lock;
#endif
	int sock;
	size_t tsize;
	struct netbuf nbuf;
	struct sockaddr_un sun;

	/*
	 * Try connecting to the local rpcbind through a local socket
	 * first. If this doesn't work, try all transports defined in
	 * the netconfig file.
	 */
	memset(&sun, 0, sizeof sun);
	sock = socket(AF_LOCAL, SOCK_STREAM, 0);
	if (sock < 0)
		goto try_nconf;
	sun.sun_family = AF_LOCAL;
	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
	nbuf.len = sun.sun_len = SUN_LEN(&sun);
	nbuf.maxlen = sizeof (struct sockaddr_un);
	nbuf.buf = &sun;

	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
	client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
	    (rpcvers_t)RPCBVERS, tsize, tsize);

	if (client != NULL) {
		/* XXX - mark the socket to be closed in destructor */
		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
		return client;
	}

	/* XXX - nobody needs this socket anymore, free the descriptor */
	rump_sys_close(sock);

try_nconf:

/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
	mutex_lock(&loopnconf_lock);
	if (loopnconf == NULL) {
		struct netconfig *nconf, *tmpnconf = NULL;
		void *nc_handle;
		int fd;

		nc_handle = setnetconfig();
		if (nc_handle == NULL) {
			/* fails to open netconfig file */
			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			mutex_unlock(&loopnconf_lock);
			return (NULL);
		}
		while ((nconf = getnetconfig(nc_handle)) != NULL) {
#ifdef INET6
			if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
#else
			if ((
#endif
			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
			    (nconf->nc_semantics == NC_TPI_COTS ||
			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
				fd = __rpc_nconf2fd(nconf);
				/*
				 * Can't create a socket, assume that
				 * this family isn't configured in the kernel.
				 */
				if (fd < 0)
					continue;
				rump_sys_close(fd);
				tmpnconf = nconf;
				if (!strcmp(nconf->nc_protofmly, NC_INET))
					hostname = IN4_LOCALHOST_STRING;
				else
					hostname = IN6_LOCALHOST_STRING;
			}
		}
		if (tmpnconf == NULL) {
			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			mutex_unlock(&loopnconf_lock);
			return (NULL);
		}
		loopnconf = getnetconfigent(tmpnconf->nc_netid);
		/* loopnconf is never freed */
		endnetconfig(nc_handle);
	}
	mutex_unlock(&loopnconf_lock);
	client = getclnthandle(hostname, loopnconf, NULL);
	return (client);
}

/*
 * Set a mapping between program, version and address.
 * Calls the rpcbind service to do the mapping.
 */
bool_t
rpcb_set(program, version, nconf, address)
	rpcprog_t program;
	rpcvers_t version;
	const struct netconfig *nconf;	/* Network structure of transport */
	const struct netbuf *address;		/* Services netconfig address */
{
	CLIENT *client;
	bool_t rslt = FALSE;
	RPCB parms;
	char uidbuf[32];

	/* parameter checking */
	if (nconf == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (FALSE);
	}
	if (address == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
		return (FALSE);
	}

	client = local_rpcb();
	if (! client) {
		return (FALSE);
	}

	/* convert to universal */
	parms.r_addr = taddr2uaddr(__UNCONST(nconf), __UNCONST(address));
	if (!parms.r_addr) {
		CLNT_DESTROY(client);
		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
		return (FALSE); /* no universal address */
	}
	parms.r_prog = program;
	parms.r_vers = version;
	parms.r_netid = nconf->nc_netid;
	/*
	 * Though uid is not being used directly, we still send it for
	 * completeness.  For non-unix platforms, perhaps some other
	 * string or an empty string can be sent.
	 */
	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
	parms.r_owner = uidbuf;

	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
	    (char *)(void *)&rslt, tottimeout);

	CLNT_DESTROY(client);
	free(parms.r_addr);
	return (rslt);
}

/*
 * Remove the mapping between program, version and netbuf address.
 * Calls the rpcbind service to do the un-mapping.
 * If netbuf is NULL, unset for all the transports, otherwise unset
 * only for the given transport.
 */
bool_t
rpcb_unset(program, version, nconf)
	rpcprog_t program;
	rpcvers_t version;
	const struct netconfig *nconf;
{
	CLIENT *client;
	bool_t rslt = FALSE;
	RPCB parms;
	char uidbuf[32];

	client = local_rpcb();
	if (! client) {
		return (FALSE);
	}

	parms.r_prog = program;
	parms.r_vers = version;
	if (nconf)
		parms.r_netid = nconf->nc_netid;
	else {
		parms.r_netid = __UNCONST(&nullstring[0]); /* unsets  all */
	}
	parms.r_addr = __UNCONST(&nullstring[0]);
	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
	parms.r_owner = uidbuf;

	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
	    (char *)(void *)&rslt, tottimeout);

	CLNT_DESTROY(client);
	return (rslt);
}

/*
 * From the merged list, find the appropriate entry
 */
static struct netbuf *
got_entry(relp, nconf)
	rpcb_entry_list_ptr relp;
	const struct netconfig *nconf;
{
	struct netbuf *na = NULL;
	rpcb_entry_list_ptr sp;
	rpcb_entry *rmap;

	_DIAGASSERT(nconf != NULL);

	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
		rmap = &sp->rpcb_entry_map;
		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
			na = uaddr2taddr(nconf, rmap->r_maddr);
#ifdef ND_DEBUG
			fprintf(stderr, "\tRemote address is [%s].\n",
				rmap->r_maddr);
			if (!na)
				fprintf(stderr,
				    "\tCouldn't resolve remote address!\n");
#endif
			break;
		}
	}
	return (na);
}

/*
 * An internal function which optimizes rpcb_getaddr function.  It also
 * returns the client handle that it uses to contact the remote rpcbind.
 *
 * The algorithm used: If the transports is TCP or UDP, it first tries
 * version 2 (portmap), 4 and then 3 (svr4).  This order should be
 * changed in the next OS release to 4, 2 and 3.  We are assuming that by
 * that time, version 4 would be available on many machines on the network.
 * With this algorithm, we get performance as well as a plan for
 * obsoleting version 2.
 *
 * For all other transports, the algorithm remains as 4 and then 3.
 *
 * XXX: Due to some problems with t_connect(), we do not reuse the same client
 * handle for COTS cases and hence in these cases we do not return the
 * client handle.  This code will change if t_connect() ever
 * starts working properly.  Also look under clnt_vc.c.
 */
struct netbuf *
__rpcb_findaddr(program, version, nconf, host, clpp)
	rpcprog_t program;
	rpcvers_t version;
	const struct netconfig *nconf;
	const char *host;
	CLIENT **clpp;
{
	CLIENT *client = NULL;
	RPCB parms;
	enum clnt_stat clnt_st;
	char *ua = NULL;
	rpcvers_t vers;
	struct netbuf *address = NULL;
	rpcvers_t start_vers = RPCBVERS4;
	struct netbuf servaddr;

	/* nconf is handled below */
	_DIAGASSERT(host != NULL);
	/* clpp may be NULL */

	/* parameter checking */
	if (nconf == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (NULL);
	}

	parms.r_addr = NULL;

#ifdef PORTMAP
	/* Try version 2 for TCP or UDP */
	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
		u_short port = 0;
		struct netbuf remote;
		rpcvers_t pmapvers = 2;
		struct pmap pmapparms;

		/*
		 * Try UDP only - there are some portmappers out
		 * there that use UDP only.
		 */
		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
			struct netconfig *newnconf;

			if ((newnconf = getnetconfigent("udp")) == NULL) {
				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
				return (NULL);
			}
			client = getclnthandle(host, newnconf, &parms.r_addr);
			freenetconfigent(newnconf);
		} else {
			client = getclnthandle(host, nconf, &parms.r_addr);
		}
		if (client == NULL) {
			return (NULL);
		}

		/* Set the version */
		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&pmapvers);
		pmapparms.pm_prog = program;
		pmapparms.pm_vers = version;
		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
					IPPROTO_UDP : IPPROTO_TCP;
		pmapparms.pm_port = 0;	/* not needed */
		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
		    tottimeout);
		if (clnt_st != RPC_SUCCESS) {
			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
				(clnt_st == RPC_PROGUNAVAIL))
				goto try_rpcbind; /* Try different versions */
			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
			clnt_geterr(client, &rpc_createerr.cf_error);
			goto error;
		} else if (port == 0) {
			address = NULL;
			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
			goto error;
		}
		port = htons(port);
		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&remote);
		if (((address = malloc(sizeof(struct netbuf))) == NULL) ||
		    ((address->buf = malloc(remote.len)) == NULL)) {
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
			clnt_geterr(client, &rpc_createerr.cf_error);
			if (address) {
				free(address);
				address = NULL;
			}
			goto error;
		}
		memcpy(address->buf, remote.buf, remote.len);
		memcpy(&((char *)address->buf)[sizeof (short)],
				(char *)(void *)&port, sizeof (short));
		address->len = address->maxlen = remote.len;
		goto done;
	}
#endif

try_rpcbind:
	/*
	 * Now we try version 4 and then 3.
	 * We also send the remote system the address we used to
	 * contact it in case it can help to connect back with us
	 */
	parms.r_prog = program;
	parms.r_vers = version;
	parms.r_owner = __UNCONST(&nullstring[0]);	/* not needed; */
							/* just for xdring */
	parms.r_netid = nconf->nc_netid; /* not really needed */

	/*
	 * If a COTS transport is being used, try getting address via CLTS
	 * transport.  This works only with version 4.
	 * NOTE: This is being done for all transports EXCEPT LOOPBACK
	 * because with loopback the cost to go to a COTS is same as
	 * the cost to go through CLTS, plus you get the advantage of
	 * finding out immediately if the local rpcbind process is dead.
	 */
#if 1
	if ((nconf->nc_semantics == NC_TPI_COTS_ORD ||
			nconf->nc_semantics == NC_TPI_COTS) &&
	    (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0))
#else
	if (client != NULL) {
		CLNT_DESTROY(client);
		client = NULL;
	}
	if (nconf->nc_semantics == NC_TPI_CLTS)
#endif
	{
		void *handle;
		struct netconfig *nconf_clts;
		rpcb_entry_list_ptr relp = NULL;

		if (client == NULL) {
			/* This did not go through the above PORTMAP/TCP code */
#if 1
			if ((handle = __rpc_setconf("datagram_v")) != NULL)
#else
			if ((handle = __rpc_setconf("circuit_v")) != NULL)
#endif
			{
				while ((nconf_clts = __rpc_getconf(handle))
					!= NULL) {
					if (strcmp(nconf_clts->nc_protofmly,
						nconf->nc_protofmly) != 0) {
						continue;
					}
					client = getclnthandle(host, nconf_clts,
							&parms.r_addr);
					break;
				}
				__rpc_endconf(handle);
			}
			if (client == NULL)
				goto regular_rpcbind;	/* Go the regular way */
		} else {
			/* This is a UDP PORTMAP handle.  Change to version 4 */
			vers = RPCBVERS4;
			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
		}
		/*
		 * We also send the remote system the address we used to
		 * contact it in case it can help it connect back with us
		 */
		if (parms.r_addr == NULL) {
			/* for XDRing */
			parms.r_addr = __UNCONST(&nullstring[0]); 
		}
		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
		    (char *)(void *)&relp, tottimeout);
		if (clnt_st == RPC_SUCCESS) {
			if ((address = got_entry(relp, nconf)) != NULL) {
				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
				    (char *)(void *)&relp);
				CLNT_CONTROL(client, CLGET_SVC_ADDR,
					(char *)(void *)&servaddr);
				__rpc_fixup_addr(address, &servaddr);
				goto done;
			}
			/* Entry not found for this transport */
			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
			    (char *)(void *)&relp);
			/*
			 * XXX: should have perhaps returned with error but
			 * since the remote machine might not always be able
			 * to send the address on all transports, we try the
			 * regular way with regular_rpcbind
			 */
			goto regular_rpcbind;
		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
			(clnt_st == RPC_PROGUNAVAIL)) {
			start_vers = RPCBVERS;	/* Try version 3 now */
			goto regular_rpcbind; /* Try different versions */
		} else {
			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
			clnt_geterr(client, &rpc_createerr.cf_error);
			goto error;
		}
	}

regular_rpcbind:

	/* Now the same transport is to be used to get the address */
#if 1
	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
			(nconf->nc_semantics == NC_TPI_COTS)))
#else
	if (client && nconf->nc_semantics == NC_TPI_CLTS)
#endif
	{
		/* A CLTS type of client - destroy it */
		CLNT_DESTROY(client);
		client = NULL;
	}

	if (client == NULL) {
		client = getclnthandle(host, nconf, &parms.r_addr);
		if (client == NULL) {
			goto error;
		}
	}
	if (parms.r_addr == NULL)
		parms.r_addr = __UNCONST(&nullstring[0]);

	/* First try from start_vers and then version 3 (RPCBVERS) */
	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
		/* Set the version */
		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua,
		    tottimeout);
		if (clnt_st == RPC_SUCCESS) {
			if ((ua == NULL) || (ua[0] == 0)) {
				/* address unknown */
				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
				goto error;
			}
			address = uaddr2taddr(nconf, ua);
#ifdef ND_DEBUG
			fprintf(stderr, "\tRemote address is [%s]\n", ua);
			if (!address)
				fprintf(stderr,
					"\tCouldn't resolve remote address!\n");
#endif
			xdr_free((xdrproc_t)xdr_wrapstring,
			    (char *)(void *)&ua);

			if (! address) {
				/* We don't know about your universal address */
				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
				goto error;
			}
			CLNT_CONTROL(client, CLGET_SVC_ADDR,
			    (char *)(void *)&servaddr);
			__rpc_fixup_addr(address, &servaddr);
			goto done;
		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
			struct rpc_err rpcerr;

			clnt_geterr(client, &rpcerr);
			if (rpcerr.re_vers.low > RPCBVERS4)
				goto error;  /* a new version, can't handle */
		} else if (clnt_st != RPC_PROGUNAVAIL) {
			/* Cant handle this error */
			rpc_createerr.cf_stat = clnt_st;
			clnt_geterr(client, &rpc_createerr.cf_error);
			goto error;
		}
	}

error:
	if (client) {
		CLNT_DESTROY(client);
		client = NULL;
	}
done:
	if (nconf->nc_semantics != NC_TPI_CLTS) {
		/* This client is the connectionless one */
		if (client) {
			CLNT_DESTROY(client);
			client = NULL;
		}
	}
	if (clpp) {
		*clpp = client;
	} else if (client) {
		CLNT_DESTROY(client);
	}
	return (address);
}


/*
 * Find the mapped address for program, version.
 * Calls the rpcbind service remotely to do the lookup.
 * Uses the transport specified in nconf.
 * Returns FALSE (0) if no map exists, else returns 1.
 *
 * Assuming that the address is all properly allocated
 */
int
rpcb_getaddr(program, version, nconf, address, host)
	rpcprog_t program;
	rpcvers_t version;
	const struct netconfig *nconf;
	struct netbuf *address;
	const char *host;
{
	struct netbuf *na;

	_DIAGASSERT(address != NULL);

	if ((na = __rpcb_findaddr(program, version, nconf,
				host, NULL)) == NULL)
		return (FALSE);

	if (na->len > address->maxlen) {
		/* Too long address */
		free(na->buf);
		free(na);
		rpc_createerr.cf_stat = RPC_FAILED;
		return (FALSE);
	}
	memcpy(address->buf, na->buf, (size_t)na->len);
	address->len = na->len;
	free(na->buf);
	free(na);
	return (TRUE);
}

/*
 * Get a copy of the current maps.
 * Calls the rpcbind service remotely to get the maps.
 *
 * It returns only a list of the services
 * It returns NULL on failure.
 */
rpcblist *
rpcb_getmaps(nconf, host)
	const struct netconfig *nconf;
	const char *host;
{
	rpcblist_ptr head = NULL;
	CLIENT *client;
	enum clnt_stat clnt_st;
	rpcvers_t vers = 0;

	client = getclnthandle(host, nconf, NULL);
	if (client == NULL) {
		return (head);
	}
	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
	    (char *)(void *)&head, tottimeout);
	if (clnt_st == RPC_SUCCESS)
		goto done;

	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
	    (clnt_st != RPC_PROGUNAVAIL)) {
		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
		clnt_geterr(client, &rpc_createerr.cf_error);
		goto done;
	}

	/* fall back to earlier version */
	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
	if (vers == RPCBVERS4) {
		vers = RPCBVERS;
		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
			goto done;
	}
	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
	clnt_geterr(client, &rpc_createerr.cf_error);

done:
	CLNT_DESTROY(client);
	return (head);
}

/*
 * rpcbinder remote-call-service interface.
 * This routine is used to call the rpcbind remote call service
 * which will look up a service program in the address maps, and then
 * remotely call that routine with the given parameters. This allows
 * programs to do a lookup and call in one step.
*/
enum clnt_stat
rpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
		xdrres, resp, tout, addr_ptr)
	const struct netconfig *nconf;	/* Netconfig structure */
	const char *host;			/* Remote host name */
	rpcprog_t prog;
	rpcvers_t vers;
	rpcproc_t proc;			/* Remote proc identifiers */
	xdrproc_t xdrargs, xdrres;	/* XDR routines */
	const char *argsp;		/* Argument */
	caddr_t resp;			/* Result */
	struct timeval tout;		/* Timeout value for this call */
	const struct netbuf *addr_ptr;	/* Preallocated netbuf address */
{
	CLIENT *client;
	enum clnt_stat stat;
	struct r_rpcb_rmtcallargs a;
	struct r_rpcb_rmtcallres r;
	rpcvers_t rpcb_vers;

	stat = RPC_FAILED;	/* XXXGCC -Wuninitialized [dreamcast] */

	client = getclnthandle(host, nconf, NULL);
	if (client == NULL) {
		return (RPC_FAILED);
	}
	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, __UNCONST(&rmttimeout));
	a.prog = prog;
	a.vers = vers;
	a.proc = proc;
	a.args.args_val = argsp;
	a.xdr_args = xdrargs;
	r.addr = NULL;
	r.results.results_val = resp;
	r.xdr_res = xdrres;

	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
			struct netbuf *na;
			na = uaddr2taddr(__UNCONST(nconf), r.addr);
			if (!na) {
				stat = RPC_N2AXLATEFAILURE;
				((struct netbuf *)__UNCONST(addr_ptr))->len = 0;
				goto error;
			}
			if (na->len > addr_ptr->maxlen) {
				/* Too long address */
				stat = RPC_FAILED; /* XXX A better error no */
				free(na->buf);
				free(na);
				((struct netbuf *)__UNCONST(addr_ptr))->len = 0;
				goto error;
			}
			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
			((struct netbuf *)__UNCONST(addr_ptr))->len = na->len;
			free(na->buf);
			free(na);
			break;
		} else if ((stat != RPC_PROGVERSMISMATCH) &&
			    (stat != RPC_PROGUNAVAIL)) {
			goto error;
		}
	}
error:
	CLNT_DESTROY(client);
	if (r.addr)
		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
	return (stat);
}

/*
 * Gets the time on the remote host.
 * Returns 1 if succeeds else 0.
 */
bool_t
rpcb_gettime(host, timep)
	const char *host;
	time_t *timep;
{
	CLIENT *client = NULL;
	void *handle;
	struct netconfig *nconf;
	rpcvers_t vers;
	enum clnt_stat st;


	if ((host == NULL) || (host[0] == 0)) {
		time(timep);
		return (TRUE);
	}

	if ((handle = __rpc_setconf("netpath")) == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (FALSE);
	}
	rpc_createerr.cf_stat = RPC_SUCCESS;
	while (client == NULL) {
		if ((nconf = __rpc_getconf(handle)) == NULL) {
			if (rpc_createerr.cf_stat == RPC_SUCCESS)
				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			break;
		}
		client = getclnthandle(host, nconf, NULL);
		if (client)
			break;
	}
	__rpc_endconf(handle);
	if (client == NULL) {
		return (FALSE);
	}

	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
		(xdrproc_t) xdr_void, NULL,
		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);

	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
		if (vers == RPCBVERS4) {
			/* fall back to earlier version */
			vers = RPCBVERS;
			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
				(xdrproc_t) xdr_void, NULL,
				(xdrproc_t) xdr_int, (char *)(void *)timep,
				tottimeout);
		}
	}
	CLNT_DESTROY(client);
	return (st == RPC_SUCCESS? TRUE: FALSE);
}

/*
 * Converts taddr to universal address.  This routine should never
 * really be called because local n2a libraries are always provided.
 */
char *
rpcb_taddr2uaddr(nconf, taddr)
	struct netconfig *nconf;
	struct netbuf *taddr;
{
	CLIENT *client;
	char *uaddr = NULL;


	/* parameter checking */
	if (nconf == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (NULL);
	}
	if (taddr == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
		return (NULL);
	}
	client = local_rpcb();
	if (! client) {
		return (NULL);
	}

	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
	CLNT_DESTROY(client);
	return (uaddr);
}

/*
 * Converts universal address to netbuf.  This routine should never
 * really be called because local n2a libraries are always provided.
 */
struct netbuf *
rpcb_uaddr2taddr(nconf, uaddr)
	struct netconfig *nconf;
	char *uaddr;
{
	CLIENT *client;
	struct netbuf *taddr;


	/* parameter checking */
	if (nconf == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (NULL);
	}
	if (uaddr == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
		return (NULL);
	}
	client = local_rpcb();
	if (! client) {
		return (NULL);
	}

	taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
	if (taddr == NULL) {
		CLNT_DESTROY(client);
		return (NULL);
	}
	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
	    tottimeout) != RPC_SUCCESS) {
		free(taddr);
		taddr = NULL;
	}
	CLNT_DESTROY(client);
	return (taddr);
}

File Added: src/tests/fs/common/nfsrpc/Attic/svc.c
/*	$NetBSD: svc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint) 
#if 0
static char *sccsid = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";
static char *sccsid = "@(#)svc.c	2.4 88/08/11 4.0 RPCSRC";
#else
__RCSID("$NetBSD: svc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * svc.c, Server-side remote procedure call interface.
 *
 * There are two sets of procedures here.  The xprt routines are
 * for handling transport handles.  The svc routines handle the
 * list of service routines.
 *
 * Copyright (C) 1984, Sun Microsystems, Inc.
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/poll.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>

#include <rpc/rpc.h>
#ifdef PORTMAP
#include <rpc/pmap_clnt.h>
#endif

#include "rpc_internal.h"
#include "svc_fdset.h"

SVCXPRT **__svc_xports;
int __svc_maxrec;

#define	RQCRED_SIZE	400		/* this size is excessive */

#define SVC_VERSQUIET 0x0001		/* keep quiet about vers mismatch */
#define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET)

#define max(a, b) (a > b ? a : b)

/*
 * The services list
 * Each entry represents a set of procedures (an rpc program).
 * The dispatch routine takes request structs and runs the
 * apropriate procedure.
 */
static struct svc_callout {
	struct svc_callout *sc_next;
	rpcprog_t	    sc_prog;
	rpcvers_t	    sc_vers;
	char		   *sc_netid;
	void		    (*sc_dispatch) __P((struct svc_req *, SVCXPRT *));
} *svc_head;

#ifdef _REENTRANT
extern rwlock_t svc_lock;
extern rwlock_t svc_fd_lock;
#endif

static struct svc_callout *svc_find __P((rpcprog_t, rpcvers_t,
					 struct svc_callout **, char *));
static void __xprt_do_unregister __P((SVCXPRT *xprt, bool_t dolock));

#include <pthread.h>

static pthread_key_t fdsetkey;
static pthread_key_t fdmaxkey;
static fd_set thefdset;
static int thefdmax;

void
init_fdsets()
{

	pthread_key_create(&fdsetkey, NULL);
	pthread_key_create(&fdmaxkey, NULL);
}

void
alloc_fdset()
{
	fd_set *fdsetti;
	int *fdmax;

	fdsetti = malloc(sizeof(*fdsetti));
	memset(fdsetti, 0, sizeof(*fdsetti));
	pthread_setspecific(fdsetkey, fdsetti);

	fdmax = malloc(sizeof(*fdmax));
	memset(fdmax, 0, sizeof(*fdmax));
	pthread_setspecific(fdmaxkey, fdmax);
}

fd_set *
get_fdset()
{
	fd_set *rv;

	rv = pthread_getspecific(fdsetkey);
	if (rv)
		return rv;
	else
		return &thefdset;
}

int *
get_fdsetmax()
{
	int *rv;

	rv = pthread_getspecific(fdmaxkey);
	if (rv)
		return rv;
	else
		return &thefdmax;
}

/* ***************  SVCXPRT related stuff **************** */

/*
 * Activate a transport handle.
 */
void
xprt_register(xprt)
	SVCXPRT *xprt;
{
	int sock;

	_DIAGASSERT(xprt != NULL);

	sock = xprt->xp_fd;

	rwlock_wrlock(&svc_fd_lock);
	if (__svc_xports == NULL) {
		__svc_xports = mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *));
		if (__svc_xports == NULL) {
			warn("xprt_register");
			goto out;
		}
		memset(__svc_xports, '\0', FD_SETSIZE * sizeof(SVCXPRT *));
	}
	if (sock < FD_SETSIZE) {
		__svc_xports[sock] = xprt;
		FD_SET(sock, get_fdset());
		*get_fdsetmax() = max(*get_fdsetmax(), sock);
	}
out:
	rwlock_unlock(&svc_fd_lock);
}

void
xprt_unregister(SVCXPRT *xprt)
{
	__xprt_do_unregister(xprt, TRUE);
}

void
__xprt_unregister_unlocked(SVCXPRT *xprt)
{
	__xprt_do_unregister(xprt, FALSE);
}

/*
 * De-activate a transport handle. 
 */
static void
__xprt_do_unregister(xprt, dolock)
	SVCXPRT *xprt;
	bool_t dolock;
{ 
	int sock;

	_DIAGASSERT(xprt != NULL);

	sock = xprt->xp_fd;

	if (dolock)
		rwlock_wrlock(&svc_fd_lock);
	if ((sock < FD_SETSIZE) && (__svc_xports[sock] == xprt)) {
		__svc_xports[sock] = NULL;
		FD_CLR(sock, get_fdset());
		if (sock >= *get_fdsetmax()) {
			for ((*get_fdsetmax())--; *get_fdsetmax()>=0; (*get_fdsetmax())--)
				if (__svc_xports[*get_fdsetmax()])
					break;
		}
	}
	if (dolock)
		rwlock_unlock(&svc_fd_lock);
}

#include <stdio.h>

/*
 * Add a service program to the callout list.
 * The dispatch routine will be called when a rpc request for this
 * program number comes in.
 */
bool_t
svc_reg(xprt, prog, vers, dispatch, nconf)
	SVCXPRT *xprt;
	const rpcprog_t prog;
	const rpcvers_t vers;
	void (*dispatch) __P((struct svc_req *, SVCXPRT *));
	const struct netconfig *nconf;
{
	bool_t dummy;
	struct svc_callout *prev;
	struct svc_callout *s;
	struct netconfig *tnconf;
	char *netid = NULL;
	int flag = 0;

	_DIAGASSERT(xprt != NULL);
	/* XXX: dispatch may be NULL ??? */

/* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */

	if (xprt->xp_netid) {
		netid = strdup(xprt->xp_netid);
		flag = 1;
	} else if (nconf && nconf->nc_netid) {
		netid = strdup(nconf->nc_netid);
		flag = 1;
	} else if ((tnconf = __rpcgettp(xprt->xp_fd)) != NULL) {
		netid = strdup(tnconf->nc_netid);
		flag = 1;
		freenetconfigent(tnconf);
	} /* must have been created with svc_raw_create */
	if ((netid == NULL) && (flag == 1)) {
		return (FALSE);
	}

	rwlock_wrlock(&svc_lock);
	if ((s = svc_find(prog, vers, &prev, netid)) != NULL) {
		if (netid)
			free(netid);
		if (s->sc_dispatch == dispatch)
			goto rpcb_it; /* he is registering another xptr */
		rwlock_unlock(&svc_lock);
		return (FALSE);
	}
	s = mem_alloc(sizeof (struct svc_callout));
	if (s == NULL) {
		if (netid)
			free(netid);
		rwlock_unlock(&svc_lock);
		return (FALSE);
	}

	if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
		if ((((SVCXPRT *) xprt)->xp_netid = strdup(netid)) == NULL) {
			warn("svc_reg");
			mem_free(s, sizeof(struct svc_callout));
			rwlock_unlock(&svc_lock);
			return FALSE;
		}

	s->sc_prog = prog;
	s->sc_vers = vers;
	s->sc_dispatch = dispatch;
	s->sc_netid = netid;
	s->sc_next = svc_head;
	svc_head = s;

rpcb_it:
	rwlock_unlock(&svc_lock);
	/* now register the information with the local binder service */
	if (nconf) {
		dummy = rpcb_set(prog, vers, __UNCONST(nconf),
		&((SVCXPRT *) xprt)->xp_ltaddr);
		return (dummy);
	}
	return (TRUE);
}

/*
 * Remove a service program from the callout list.
 */
void
svc_unreg(prog, vers)
	const rpcprog_t prog;
	const rpcvers_t vers;
{
	struct svc_callout *prev;
	struct svc_callout *s;

	/* unregister the information anyway */
	(void) rpcb_unset(prog, vers, NULL);
	rwlock_wrlock(&svc_lock);
	while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
		if (prev == NULL) {
			svc_head = s->sc_next;
		} else {
			prev->sc_next = s->sc_next;
		}
		s->sc_next = NULL;
		if (s->sc_netid)
			mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
		mem_free(s, sizeof (struct svc_callout));
	}
	rwlock_unlock(&svc_lock);
}

/* ********************** CALLOUT list related stuff ************* */

#ifdef PORTMAP
/*
 * Add a service program to the callout list.
 * The dispatch routine will be called when a rpc request for this
 * program number comes in.
 */
bool_t
svc_register(xprt, prog, vers, dispatch, protocol)
	SVCXPRT *xprt;
	u_long prog;
	u_long vers;
	void (*dispatch) __P((struct svc_req *, SVCXPRT *));
	int protocol;
{
	struct svc_callout *prev;
	struct svc_callout *s;

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(dispatch != NULL);

	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) !=
	    NULL) {
		if (s->sc_dispatch == dispatch)
			goto pmap_it;  /* he is registering another xptr */
		return (FALSE);
	}
	s = mem_alloc(sizeof(struct svc_callout));
	if (s == NULL) {
		return (FALSE);
	}
	s->sc_prog = (rpcprog_t)prog;
	s->sc_vers = (rpcvers_t)vers;
	s->sc_dispatch = dispatch;
	s->sc_next = svc_head;
	svc_head = s;
pmap_it:
	/* now register the information with the local binder service */
	if (protocol) {
		return (pmap_set(prog, vers, protocol, xprt->xp_port));
	}
	return (TRUE);
}

/*
 * Remove a service program from the callout list.
 */
void
svc_unregister(prog, vers)
	u_long prog;
	u_long vers;
{
	struct svc_callout *prev;
	struct svc_callout *s;

	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) ==
	    NULL)
		return;
	if (prev == NULL) {
		svc_head = s->sc_next;
	} else {
		prev->sc_next = s->sc_next;
	}
	s->sc_next = NULL;
	mem_free(s, sizeof(struct svc_callout));
	/* now unregister the information with the local binder service */
	(void)pmap_unset(prog, vers);
}
#endif /* PORTMAP */

/*
 * Search the callout list for a program number, return the callout
 * struct.
 */
static struct svc_callout *
svc_find(prog, vers, prev, netid)
	rpcprog_t prog;
	rpcvers_t vers;
	struct svc_callout **prev;
	char *netid;
{
	struct svc_callout *s, *p;

	_DIAGASSERT(prev != NULL);
	/* netid is handled below */

	p = NULL;
	for (s = svc_head; s != NULL; s = s->sc_next) {
		if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
		    ((netid == NULL) || (s->sc_netid == NULL) ||
		    (strcmp(netid, s->sc_netid) == 0)))
			break;
		p = s;
	}
	*prev = p;
	return (s);
}

/* ******************* REPLY GENERATION ROUTINES  ************ */

/*
 * Send a reply to an rpc request
 */
bool_t
svc_sendreply(xprt, xdr_results, xdr_location)
	SVCXPRT *xprt;
	xdrproc_t xdr_results;
	const char *xdr_location;
{
	struct rpc_msg rply; 

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY;  
	rply.rm_reply.rp_stat = MSG_ACCEPTED; 
	rply.acpted_rply.ar_verf = xprt->xp_verf; 
	rply.acpted_rply.ar_stat = SUCCESS;
	rply.acpted_rply.ar_results.where = xdr_location;
	rply.acpted_rply.ar_results.proc = xdr_results;
	return (SVC_REPLY(xprt, &rply)); 
}

/*
 * No procedure error reply
 */
void
svcerr_noproc(xprt)
	SVCXPRT *xprt;
{
	struct rpc_msg rply;

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY;
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
	rply.acpted_rply.ar_verf = xprt->xp_verf;
	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
	SVC_REPLY(xprt, &rply);
}

/*
 * Can't decode args error reply
 */
void
svcerr_decode(xprt)
	SVCXPRT *xprt;
{
	struct rpc_msg rply; 

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY; 
	rply.rm_reply.rp_stat = MSG_ACCEPTED; 
	rply.acpted_rply.ar_verf = xprt->xp_verf;
	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
	SVC_REPLY(xprt, &rply); 
}

/*
 * Some system error
 */
void
svcerr_systemerr(xprt)
	SVCXPRT *xprt;
{
	struct rpc_msg rply; 

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY; 
	rply.rm_reply.rp_stat = MSG_ACCEPTED; 
	rply.acpted_rply.ar_verf = xprt->xp_verf;
	rply.acpted_rply.ar_stat = SYSTEM_ERR;
	SVC_REPLY(xprt, &rply); 
}

#if 0
/*
 * Tell RPC package to not complain about version errors to the client.	 This
 * is useful when revving broadcast protocols that sit on a fixed address.
 * There is really one (or should be only one) example of this kind of
 * protocol: the portmapper (or rpc binder).
 */
void
__svc_versquiet_on(xprt)
	SVCXPRT *xprt;
{
	u_long	tmp;

	_DIAGASSERT(xprt != NULL);

	tmp = ((u_long) xprt->xp_p3) | SVC_VERSQUIET;
	xprt->xp_p3 = (caddr_t) tmp;
}

void
__svc_versquiet_off(xprt)
	SVCXPRT *xprt;
{
	u_long	tmp;

	_DIAGASSERT(xprt != NULL);

	tmp = ((u_long) xprt->xp_p3) & ~SVC_VERSQUIET;
	xprt->xp_p3 = (caddr_t) tmp;
}

void
svc_versquiet(xprt)
	SVCXPRT *xprt;
{
	__svc_versquiet_on(xprt);
}

int
__svc_versquiet_get(xprt)
	SVCXPRT *xprt;
{

	_DIAGASSERT(xprt != NULL);

	return ((int) xprt->xp_p3) & SVC_VERSQUIET;
}
#endif

/*
 * Authentication error reply
 */
void
svcerr_auth(xprt, why)
	SVCXPRT *xprt;
	enum auth_stat why;
{
	struct rpc_msg rply;

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY;
	rply.rm_reply.rp_stat = MSG_DENIED;
	rply.rjcted_rply.rj_stat = AUTH_ERROR;
	rply.rjcted_rply.rj_why = why;
	SVC_REPLY(xprt, &rply);
}

/*
 * Auth too weak error reply
 */
void
svcerr_weakauth(xprt)
	SVCXPRT *xprt;
{

	_DIAGASSERT(xprt != NULL);

	svcerr_auth(xprt, AUTH_TOOWEAK);
}

/*
 * Program unavailable error reply
 */
void 
svcerr_noprog(xprt)
	SVCXPRT *xprt;
{
	struct rpc_msg rply;  

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY;   
	rply.rm_reply.rp_stat = MSG_ACCEPTED;  
	rply.acpted_rply.ar_verf = xprt->xp_verf;  
	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
	SVC_REPLY(xprt, &rply);
}

/*
 * Program version mismatch error reply
 */
void  
svcerr_progvers(xprt, low_vers, high_vers)
	SVCXPRT *xprt; 
	rpcvers_t low_vers;
	rpcvers_t high_vers;
{
	struct rpc_msg rply;

	_DIAGASSERT(xprt != NULL);

	rply.rm_direction = REPLY;
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
	rply.acpted_rply.ar_verf = xprt->xp_verf;
	rply.acpted_rply.ar_stat = PROG_MISMATCH;
	rply.acpted_rply.ar_vers.low = (u_int32_t)low_vers;
	rply.acpted_rply.ar_vers.high = (u_int32_t)high_vers;
	SVC_REPLY(xprt, &rply);
}

/* ******************* SERVER INPUT STUFF ******************* */

/*
 * Get server side input from some transport.
 *
 * Statement of authentication parameters management:
 * This function owns and manages all authentication parameters, specifically
 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
 * the "cooked" credentials (rqst->rq_clntcred).
 * However, this function does not know the structure of the cooked
 * credentials, so it make the following assumptions: 
 *   a) the structure is contiguous (no pointers), and
 *   b) the cred structure size does not exceed RQCRED_SIZE bytes. 
 * In all events, all three parameters are freed upon exit from this routine.
 * The storage is trivially management on the call stack in user land, but
 * is mallocated in kernel land.
 */

void
svc_getreq(rdfds)
	int rdfds;
{
	fd_set readfds;

	FD_ZERO(&readfds);
	readfds.fds_bits[0] = rdfds;
	svc_getreqset(&readfds);
}

void
svc_getreqset(readfds)
	fd_set *readfds;
{
	int bit, fd;
	int32_t mask, *maskp;
	int sock;

	_DIAGASSERT(readfds != NULL);

	maskp = readfds->fds_bits;
	for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) {
	    for (mask = *maskp++; (bit = ffs(mask)) != 0;
		mask ^= (1 << (bit - 1))) {
		/* sock has input waiting */
		fd = sock + bit - 1;
		svc_getreq_common(fd);
	    }
	}
}

void
svc_getreq_common(fd)
	int fd;
{
	SVCXPRT *xprt;
	struct svc_req r;
	struct rpc_msg msg;
	int prog_found;
	rpcvers_t low_vers;
	rpcvers_t high_vers;
	enum xprt_stat stat;
	char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];

	msg.rm_call.cb_cred.oa_base = cred_area;
	msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
	r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);

	rwlock_rdlock(&svc_fd_lock);
	xprt = __svc_xports[fd];
	rwlock_unlock(&svc_fd_lock);
	if (xprt == NULL)
		/* But do we control sock? */
		return;
	/* now receive msgs from xprtprt (support batch calls) */
	do {
		if (SVC_RECV(xprt, &msg)) {

			/* now find the exported program and call it */
			struct svc_callout *s;
			enum auth_stat why;

			r.rq_xprt = xprt;
			r.rq_prog = msg.rm_call.cb_prog;
			r.rq_vers = msg.rm_call.cb_vers;
			r.rq_proc = msg.rm_call.cb_proc;
			r.rq_cred = msg.rm_call.cb_cred;
			/* first authenticate the message */
			if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
				svcerr_auth(xprt, why);
				goto call_done;
			}
			/* now match message with a registered service*/
			prog_found = FALSE;
			low_vers = (rpcvers_t) -1L;
			high_vers = (rpcvers_t) 0L;
			for (s = svc_head; s != NULL; s = s->sc_next) {
				if (s->sc_prog == r.rq_prog) {
					if (s->sc_vers == r.rq_vers) {
						(*s->sc_dispatch)(&r, xprt);
						goto call_done;
					}  /* found correct version */
					prog_found = TRUE;
					if (s->sc_vers < low_vers)
						low_vers = s->sc_vers;
					if (s->sc_vers > high_vers)
						high_vers = s->sc_vers;
				}   /* found correct program */
			}
			/*
			 * if we got here, the program or version
			 * is not served ...
			 */
			if (prog_found)
				svcerr_progvers(xprt, low_vers, high_vers);
			else
				 svcerr_noprog(xprt);
			/* Fall through to ... */
		}
call_done:
		/*
		 * Check if the xprt has been disconnected in a
		 * recursive call in the service dispatch routine.
		 * If so, then break.
		 */
		rwlock_rdlock(&svc_fd_lock);
		if (xprt != __svc_xports[fd]) {
			rwlock_unlock(&svc_fd_lock);
			break;
		}
		rwlock_unlock(&svc_fd_lock);
		if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
			SVC_DESTROY(xprt);
			break;
		}
	} while (stat == XPRT_MOREREQS);
}


void
svc_getreq_poll(pfdp, pollretval)
	struct pollfd	*pfdp;
	int	pollretval;
{
	int i;
	int fds_found;

	_DIAGASSERT(pfdp != NULL);

	for (i = fds_found = 0; fds_found < pollretval; i++) {
		struct pollfd *p = &pfdp[i];

		if (p->revents) {
			/* fd has input waiting */
			fds_found++;
			/*
			 *	We assume that this function is only called
			 *	via someone select()ing from svc_fdset or
			 *	pollts()ing from svc_pollset[].  Thus it's safe
			 *	to handle the POLLNVAL event by simply turning
			 *	the corresponding bit off in svc_fdset.  The
			 *	svc_pollset[] array is derived from svc_fdset
			 *	and so will also be updated eventually.
			 *
			 *	XXX Should we do an xprt_unregister() instead?
			 */
			if (p->revents & POLLNVAL) {
				rwlock_wrlock(&svc_fd_lock);
				FD_CLR(p->fd, get_fdset());
				rwlock_unlock(&svc_fd_lock);
			} else
				svc_getreq_common(p->fd);
		}
	}
}

bool_t
rpc_control(int what, void *arg)
{
	int val;

	switch (what) {
	case RPC_SVC_CONNMAXREC_SET:
		val = *(int *)arg;
		if (val <= 0)
			return FALSE;
		__svc_maxrec = val;
		return TRUE;
	case RPC_SVC_CONNMAXREC_GET:
		*(int *)arg = __svc_maxrec;
		return TRUE;
	default:
		break;
	}
	return FALSE;
}

File Added: src/tests/fs/common/nfsrpc/Attic/svc_dg.c
/*	$NetBSD: svc_dg.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
 */

/* #ident	"@(#)svc_dg.c	1.17	94/04/24 SMI" */


/*
 * svc_dg.c, Server side for connectionless RPC.
 *
 * Does some caching in the hopes of achieving execute-at-most-once semantics.
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: svc_dg.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <rpc/rpc.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef RPC_CACHE_DEBUG
#include <netconfig.h>
#include <netdir.h>
#endif
#include <err.h>

#include "rpc_internal.h"
#include "svc_dg.h"

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#define	su_data(xprt)	((struct svc_dg_data *)(xprt->xp_p2))
#define	rpc_buffer(xprt) ((xprt)->xp_p1)

#ifndef MAX
#define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
#endif

static void svc_dg_ops __P((SVCXPRT *));
static enum xprt_stat svc_dg_stat __P((SVCXPRT *));
static bool_t svc_dg_recv __P((SVCXPRT *, struct rpc_msg *));
static bool_t svc_dg_reply __P((SVCXPRT *, struct rpc_msg *));
static bool_t svc_dg_getargs __P((SVCXPRT *, xdrproc_t, caddr_t));
static bool_t svc_dg_freeargs __P((SVCXPRT *, xdrproc_t, caddr_t));
static void svc_dg_destroy __P((SVCXPRT *));
static bool_t svc_dg_control __P((SVCXPRT *, const u_int, void *));
static int cache_get __P((SVCXPRT *, struct rpc_msg *, char **, size_t *));
static void cache_set __P((SVCXPRT *, size_t));

/*
 * Usage:
 *	xprt = svc_dg_create(sock, sendsize, recvsize);
 * Does other connectionless specific initializations.
 * Once *xprt is initialized, it is registered.
 * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable
 * system defaults are chosen.
 * The routines returns NULL if a problem occurred.
 */
static const char svc_dg_str[] = "svc_dg_create: %s";
static const char svc_dg_err1[] = "could not get transport information";
static const char svc_dg_err2[] = " transport does not support data transfer";
static const char __no_mem_str[] = "out of memory";

SVCXPRT *
svc_dg_create(fd, sendsize, recvsize)
	int fd;
	u_int sendsize;
	u_int recvsize;
{
	SVCXPRT *xprt;
	struct svc_dg_data *su = NULL;
	struct __rpc_sockinfo si;
	struct sockaddr_storage ss;
	socklen_t slen;

	if (!__rpc_fd2sockinfo(fd, &si)) {
		warnx(svc_dg_str, svc_dg_err1);
		return (NULL);
	}
	/*
	 * Find the receive and the send size
	 */
	sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
	recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
	if ((sendsize == 0) || (recvsize == 0)) {
		warnx(svc_dg_str, svc_dg_err2);
		return (NULL);
	}

	xprt = mem_alloc(sizeof (SVCXPRT));
	if (xprt == NULL)
		goto freedata;
	memset(xprt, 0, sizeof (SVCXPRT));

	su = mem_alloc(sizeof (*su));
	if (su == NULL)
		goto freedata;
	su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4;
	if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL)
		goto freedata;
	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
		XDR_DECODE);
	su->su_cache = NULL;
	xprt->xp_fd = fd;
	xprt->xp_p2 = (caddr_t)(void *)su;
	xprt->xp_verf.oa_base = su->su_verfbody;
	svc_dg_ops(xprt);
	xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);

	slen = sizeof ss;
	if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0)
		goto freedata;
	xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
	xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage);
	xprt->xp_ltaddr.len = slen;
	memcpy(xprt->xp_ltaddr.buf, &ss, slen);

	xprt_register(xprt);

	return (xprt);
freedata:
	(void) warnx(svc_dg_str, __no_mem_str);
	if (xprt) {
		if (su)
			(void) mem_free(su, sizeof (*su));
		(void) mem_free(xprt, sizeof (SVCXPRT));
	}
	return (NULL);
}

/*ARGSUSED*/
static enum xprt_stat
svc_dg_stat(xprt)
	SVCXPRT *xprt;
{
	return (XPRT_IDLE);
}

static bool_t
svc_dg_recv(xprt, msg)
	SVCXPRT *xprt;
	struct rpc_msg *msg;
{
	struct svc_dg_data *su;
	XDR *xdrs;
	char *reply;
	struct sockaddr_storage ss;
	socklen_t alen;
	size_t replylen;
	ssize_t rlen;

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(msg != NULL);

	su = su_data(xprt);
	xdrs = &(su->su_xdrs);

again:
	alen = sizeof (struct sockaddr_storage);
	rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0,
	    (struct sockaddr *)(void *)&ss, &alen);
	if (rlen == -1 && errno == EINTR)
		goto again;
	if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t))))
		return (FALSE);
	if (xprt->xp_rtaddr.len < alen) {
		if (xprt->xp_rtaddr.len != 0)
			mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.len);
		xprt->xp_rtaddr.buf = mem_alloc(alen);
		xprt->xp_rtaddr.len = alen;
	}
	memcpy(xprt->xp_rtaddr.buf, &ss, alen);
#ifdef PORTMAP
	if (ss.ss_family == AF_INET) {
		xprt->xp_raddr = *(struct sockaddr_in *)xprt->xp_rtaddr.buf;
		xprt->xp_addrlen = sizeof (struct sockaddr_in);
	}
#endif
	xdrs->x_op = XDR_DECODE;
	XDR_SETPOS(xdrs, 0);
	if (! xdr_callmsg(xdrs, msg)) {
		return (FALSE);
	}
	su->su_xid = msg->rm_xid;
	if (su->su_cache != NULL) {
		if (cache_get(xprt, msg, &reply, &replylen)) {
			(void)sendto(xprt->xp_fd, reply, replylen, 0,
			    (struct sockaddr *)(void *)&ss, alen);
			return (FALSE);
		}
	}
	return (TRUE);
}

static bool_t
svc_dg_reply(xprt, msg)
	SVCXPRT *xprt;
	struct rpc_msg *msg;
{
	struct svc_dg_data *su;
	XDR *xdrs;
	bool_t stat = FALSE;
	size_t slen;

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(msg != NULL);

	su = su_data(xprt);
	xdrs = &(su->su_xdrs);

	xdrs->x_op = XDR_ENCODE;
	XDR_SETPOS(xdrs, 0);
	msg->rm_xid = su->su_xid;
	if (xdr_replymsg(xdrs, msg)) {
		slen = XDR_GETPOS(xdrs);
		if (sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0,
		    (struct sockaddr *)xprt->xp_rtaddr.buf,
		    (socklen_t)xprt->xp_rtaddr.len) == (ssize_t) slen) {
			stat = TRUE;
			if (su->su_cache)
				cache_set(xprt, slen);
		}
	}
	return (stat);
}

static bool_t
svc_dg_getargs(xprt, xdr_args, args_ptr)
	SVCXPRT *xprt;
	xdrproc_t xdr_args;
	caddr_t args_ptr;
{
	return (*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr);
}

static bool_t
svc_dg_freeargs(xprt, xdr_args, args_ptr)
	SVCXPRT *xprt;
	xdrproc_t xdr_args;
	caddr_t args_ptr;
{
	XDR *xdrs;

	_DIAGASSERT(xprt != NULL);

	xdrs = &(su_data(xprt)->su_xdrs);
	xdrs->x_op = XDR_FREE;
	return (*xdr_args)(xdrs, args_ptr);
}

static void
svc_dg_destroy(xprt)
	SVCXPRT *xprt;
{
	struct svc_dg_data *su;

	_DIAGASSERT(xprt != NULL);

	su = su_data(xprt);

	xprt_unregister(xprt);
	if (xprt->xp_fd != -1)
		(void)rump_sys_close(xprt->xp_fd);
	XDR_DESTROY(&(su->su_xdrs));
	(void) mem_free(rpc_buffer(xprt), su->su_iosz);
	(void) mem_free(su, sizeof (*su));
	if (xprt->xp_rtaddr.buf)
		(void) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
	if (xprt->xp_ltaddr.buf)
		(void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
	if (xprt->xp_tp)
		(void) free(xprt->xp_tp);
	(void) mem_free(xprt, sizeof (SVCXPRT));
}

static bool_t
/*ARGSUSED*/
svc_dg_control(xprt, rq, in)
	SVCXPRT *xprt;
	const u_int	rq;
	void		*in;
{
	return (FALSE);
}

static void
svc_dg_ops(xprt)
	SVCXPRT *xprt;
{
	static struct xp_ops ops;
	static struct xp_ops2 ops2;
#ifdef _REENTRANT
	extern mutex_t ops_lock;
#endif

	_DIAGASSERT(xprt != NULL);

/* VARIABLES PROTECTED BY ops_lock: ops */

	mutex_lock(&ops_lock);
	if (ops.xp_recv == NULL) {
		ops.xp_recv = svc_dg_recv;
		ops.xp_stat = svc_dg_stat;
		ops.xp_getargs = svc_dg_getargs;
		ops.xp_reply = svc_dg_reply;
		ops.xp_freeargs = svc_dg_freeargs;
		ops.xp_destroy = svc_dg_destroy;
		ops2.xp_control = svc_dg_control;
	}
	xprt->xp_ops = &ops;
	xprt->xp_ops2 = &ops2;
	mutex_unlock(&ops_lock);
}

/*  The CACHING COMPONENT */

/*
 * Could have been a separate file, but some part of it depends upon the
 * private structure of the client handle.
 *
 * Fifo cache for cl server
 * Copies pointers to reply buffers into fifo cache
 * Buffers are sent again if retransmissions are detected.
 */

#define	SPARSENESS 4	/* 75% sparse */

#define	ALLOC(type, size)	\
	mem_alloc((sizeof (type) * (size)))

#define	MEMZERO(addr, type, size)	 \
	(void) memset((void *) (addr), 0, sizeof (type) * (int) (size))

#define	FREE(addr, type, size)	\
	mem_free((addr), (sizeof (type) * (size)))

/*
 * An entry in the cache
 */
typedef struct cache_node *cache_ptr;
struct cache_node {
	/*
	 * Index into cache is xid, proc, vers, prog and address
	 */
	u_int32_t cache_xid;
	rpcproc_t cache_proc;
	rpcvers_t cache_vers;
	rpcprog_t cache_prog;
	struct netbuf cache_addr;
	/*
	 * The cached reply and length
	 */
	char *cache_reply;
	size_t cache_replylen;
	/*
	 * Next node on the list, if there is a collision
	 */
	cache_ptr cache_next;
};

/*
 * The entire cache
 */
struct cl_cache {
	u_int uc_size;		/* size of cache */
	cache_ptr *uc_entries;	/* hash table of entries in cache */
	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
	u_int uc_nextvictim;	/* points to next victim in fifo list */
	rpcprog_t uc_prog;	/* saved program number */
	rpcvers_t uc_vers;	/* saved version number */
	rpcproc_t uc_proc;	/* saved procedure number */
};


/*
 * the hashing function
 */
#define	CACHE_LOC(transp, xid)	\
	(xid % (SPARSENESS * ((struct cl_cache *) \
		su_data(transp)->su_cache)->uc_size))

#ifdef _REENTRANT
extern mutex_t	dupreq_lock;
#endif

/*
 * Enable use of the cache. Returns 1 on success, 0 on failure.
 * Note: there is no disable.
 */
static const char cache_enable_str[] = "svc_enablecache: %s %s";
static const char alloc_err[] = "could not allocate cache ";
static const char enable_err[] = "cache already enabled";

int
svc_dg_enablecache(transp, size)
	SVCXPRT *transp;
	u_int size;
{
	struct svc_dg_data *su;
	struct cl_cache *uc;

	_DIAGASSERT(transp != NULL);

	su = su_data(transp);

	mutex_lock(&dupreq_lock);
	if (su->su_cache != NULL) {
		(void) warnx(cache_enable_str, enable_err, " ");
		mutex_unlock(&dupreq_lock);
		return (0);
	}
	uc = ALLOC(struct cl_cache, 1);
	if (uc == NULL) {
		warnx(cache_enable_str, alloc_err, " ");
		mutex_unlock(&dupreq_lock);
		return (0);
	}
	uc->uc_size = size;
	uc->uc_nextvictim = 0;
	uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
	if (uc->uc_entries == NULL) {
		warnx(cache_enable_str, alloc_err, "data");
		FREE(uc, struct cl_cache, 1);
		mutex_unlock(&dupreq_lock);
		return (0);
	}
	MEMZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
	uc->uc_fifo = ALLOC(cache_ptr, size);
	if (uc->uc_fifo == NULL) {
		warnx(cache_enable_str, alloc_err, "fifo");
		FREE(uc->uc_entries, cache_ptr, size * SPARSENESS);
		FREE(uc, struct cl_cache, 1);
		mutex_unlock(&dupreq_lock);
		return (0);
	}
	MEMZERO(uc->uc_fifo, cache_ptr, size);
	su->su_cache = (char *)(void *)uc;
	mutex_unlock(&dupreq_lock);
	return (1);
}

/*
 * Set an entry in the cache.  It assumes that the uc entry is set from
 * the earlier call to cache_get() for the same procedure.  This will always
 * happen because cache_get() is calle by svc_dg_recv and cache_set() is called
 * by svc_dg_reply().  All this hoopla because the right RPC parameters are
 * not available at svc_dg_reply time.
 */

static const char cache_set_str[] = "cache_set: %s";
static const char cache_set_err1[] = "victim not found";
static const char cache_set_err2[] = "victim alloc failed";
static const char cache_set_err3[] = "could not allocate new rpc buffer";

static void
cache_set(xprt, replylen)
	SVCXPRT *xprt;
	size_t replylen;
{
	cache_ptr victim;
	cache_ptr *vicp;
	struct svc_dg_data *su;
	struct cl_cache *uc;
	u_int loc;
	char *newbuf;
#ifdef RPC_CACHE_DEBUG
	struct netconfig *nconf;
	char *uaddr;
#endif

	_DIAGASSERT(xprt != NULL);

	su = su_data(xprt);
	uc = (struct cl_cache *) su->su_cache;

	mutex_lock(&dupreq_lock);
	/*
	 * Find space for the new entry, either by
	 * reusing an old entry, or by mallocing a new one
	 */
	victim = uc->uc_fifo[uc->uc_nextvictim];
	if (victim != NULL) {
		loc = CACHE_LOC(xprt, victim->cache_xid);
		for (vicp = &uc->uc_entries[loc];
			*vicp != NULL && *vicp != victim;
			vicp = &(*vicp)->cache_next)
			;
		if (*vicp == NULL) {
			warnx(cache_set_str, cache_set_err1);
			mutex_unlock(&dupreq_lock);
			return;
		}
		*vicp = victim->cache_next;	/* remove from cache */
		newbuf = victim->cache_reply;
	} else {
		victim = ALLOC(struct cache_node, 1);
		if (victim == NULL) {
			warnx(cache_set_str, cache_set_err2);
			mutex_unlock(&dupreq_lock);
			return;
		}
		newbuf = mem_alloc(su->su_iosz);
		if (newbuf == NULL) {
			warnx(cache_set_str, cache_set_err3);
			FREE(victim, struct cache_node, 1);
			mutex_unlock(&dupreq_lock);
			return;
		}
	}

	/*
	 * Store it away
	 */
#ifdef RPC_CACHE_DEBUG
	if (nconf = getnetconfigent(xprt->xp_netid)) {
		uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
		freenetconfigent(nconf);
		printf(
	"cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
			su->su_xid, uc->uc_prog, uc->uc_vers,
			uc->uc_proc, uaddr);
		free(uaddr);
	}
#endif
	victim->cache_replylen = replylen;
	victim->cache_reply = rpc_buffer(xprt);
	rpc_buffer(xprt) = newbuf;
	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt),
			su->su_iosz, XDR_ENCODE);
	victim->cache_xid = su->su_xid;
	victim->cache_proc = uc->uc_proc;
	victim->cache_vers = uc->uc_vers;
	victim->cache_prog = uc->uc_prog;
	victim->cache_addr = xprt->xp_rtaddr;
	victim->cache_addr.buf = ALLOC(char, xprt->xp_rtaddr.len);
	(void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf,
	    (size_t)xprt->xp_rtaddr.len);
	loc = CACHE_LOC(xprt, victim->cache_xid);
	victim->cache_next = uc->uc_entries[loc];
	uc->uc_entries[loc] = victim;
	uc->uc_fifo[uc->uc_nextvictim++] = victim;
	uc->uc_nextvictim %= uc->uc_size;
	mutex_unlock(&dupreq_lock);
}

/*
 * Try to get an entry from the cache
 * return 1 if found, 0 if not found and set the stage for cache_set()
 */
static int
cache_get(xprt, msg, replyp, replylenp)
	SVCXPRT *xprt;
	struct rpc_msg *msg;
	char **replyp;
	size_t *replylenp;
{
	u_int loc;
	cache_ptr ent;
	struct svc_dg_data *su;
	struct cl_cache *uc;
#ifdef RPC_CACHE_DEBUG
	struct netconfig *nconf;
	char *uaddr;
#endif

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(msg != NULL);
	_DIAGASSERT(replyp != NULL);
	_DIAGASSERT(replylenp != NULL);

	su = su_data(xprt);
	uc = (struct cl_cache *) su->su_cache;

	mutex_lock(&dupreq_lock);
	loc = CACHE_LOC(xprt, su->su_xid);
	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
		if (ent->cache_xid == su->su_xid &&
			ent->cache_proc == msg->rm_call.cb_proc &&
			ent->cache_vers == msg->rm_call.cb_vers &&
			ent->cache_prog == msg->rm_call.cb_prog &&
			ent->cache_addr.len == xprt->xp_rtaddr.len &&
			(memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf,
				xprt->xp_rtaddr.len) == 0)) {
#ifdef RPC_CACHE_DEBUG
			if (nconf = getnetconfigent(xprt->xp_netid)) {
				uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
				freenetconfigent(nconf);
				printf(
	"cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
					su->su_xid, msg->rm_call.cb_prog,
					msg->rm_call.cb_vers,
					msg->rm_call.cb_proc, uaddr);
				free(uaddr);
			}
#endif
			*replyp = ent->cache_reply;
			*replylenp = ent->cache_replylen;
			mutex_unlock(&dupreq_lock);
			return (1);
		}
	}
	/*
	 * Failed to find entry
	 * Remember a few things so we can do a set later
	 */
	uc->uc_proc = msg->rm_call.cb_proc;
	uc->uc_vers = msg->rm_call.cb_vers;
	uc->uc_prog = msg->rm_call.cb_prog;
	mutex_unlock(&dupreq_lock);
	return (0);
}

File Added: src/tests/fs/common/nfsrpc/Attic/svc_dg.h
/*	$NetBSD: svc_dg.h,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/
/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/*
 * XXX - this file exists only so that the rpcbind code can pull it in.
 * This should go away. It should only be include by svc_dg.c and
 * rpcb_svc_com.c in the rpcbind code.
 */

/*
 * kept in xprt->xp_p2
 */
struct svc_dg_data {
	/* XXX: optbuf should be the first field, used by ti_opts.c code */
	size_t		su_iosz;		/* size of send.recv buffer */
	u_int32_t	su_xid;			/* transaction id */
	XDR		su_xdrs;			/* XDR handle */
	char		su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
	void		*su_cache;		/* cached data, NULL if none */
};

#define __rpcb_get_dg_xidp(x)	(&((struct svc_dg_data *)(x)->xp_p2)->su_xid)

File Added: src/tests/fs/common/nfsrpc/Attic/svc_fdset.h
/*	$NetBSD: svc_fdset.h,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

void init_fdsets(void);
void alloc_fdset(void);
fd_set *get_fdset(void);
int *get_fdsetmax(void);

File Added: src/tests/fs/common/nfsrpc/Attic/svc_generic.c
/*	$NetBSD: svc_generic.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/*
 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
 */

/* #ident	"@(#)svc_generic.c	1.19	94/04/24 SMI" */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)svc_generic.c 1.21 89/02/28 Copyr 1988 Sun Micro";
#else
__RCSID("$NetBSD: svc_generic.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * svc_generic.c, Server side for RPC.
 *
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/nettype.h>
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include <err.h>

#include "rpc_internal.h"

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

extern int __svc_vc_setflag __P((SVCXPRT *, int));

/*
 * The highest level interface for server creation.
 * It tries for all the nettokens in that particular class of token
 * and returns the number of handles it can create and/or find.
 *
 * It creates a link list of all the handles it could create.
 * If svc_create() is called multiple times, it uses the handle
 * created earlier instead of creating a new handle every time.
 */
int
svc_create(dispatch, prognum, versnum, nettype)
	void (*dispatch) __P((struct svc_req *, SVCXPRT *));
	rpcprog_t prognum;		/* Program number */
	rpcvers_t versnum;		/* Version number */
	const char *nettype;		/* Networktype token */
{
	struct xlist {
		SVCXPRT *xprt;		/* Server handle */
		struct xlist *next;	/* Next item */
	} *l;
	static struct xlist *xprtlist;	/* A link list of all the handles */
	int num = 0;
	SVCXPRT *xprt;
	struct netconfig *nconf;
	void *handle;
#ifdef _REENTRANT
	extern mutex_t xprtlist_lock;
#endif

/* VARIABLES PROTECTED BY xprtlist_lock: xprtlist */

	if ((handle = __rpc_setconf(nettype)) == NULL) {
		warnx("svc_create: unknown protocol");
		return (0);
	}
	while ((nconf = __rpc_getconf(handle)) != NULL) {
		mutex_lock(&xprtlist_lock);
		for (l = xprtlist; l; l = l->next) {
			if (strcmp(l->xprt->xp_netid, nconf->nc_netid) == 0) {
				/* Found an old one, use it */
				(void) rpcb_unset(prognum, versnum, nconf);
				if (svc_reg(l->xprt, prognum, versnum,
					dispatch, nconf) == FALSE)
					warnx(
		"svc_create: could not register prog %u vers %u on %s",
					(unsigned)prognum, (unsigned)versnum,
					 nconf->nc_netid);
				else
					num++;
				break;
			}
		}
		if (l == NULL) {
			/* It was not found. Now create a new one */
			xprt = svc_tp_create(dispatch, prognum, versnum, nconf);
			if (xprt) {
				l = malloc(sizeof(*l));
				if (l == NULL) {
					warnx("svc_create: no memory");
					mutex_unlock(&xprtlist_lock);
					return (0);
				}
				l->xprt = xprt;
				l->next = xprtlist;
				xprtlist = l;
				num++;
			}
		}
		mutex_unlock(&xprtlist_lock);
	}
	__rpc_endconf(handle);
	/*
	 * In case of num == 0; the error messages are generated by the
	 * underlying layers; and hence not needed here.
	 */
	return (num);
}

/*
 * The high level interface to svc_tli_create().
 * It tries to create a server for "nconf" and registers the service
 * with the rpcbind. It calls svc_tli_create();
 */
SVCXPRT *
svc_tp_create(dispatch, prognum, versnum, nconf)
	void (*dispatch) __P((struct svc_req *, SVCXPRT *));
	rpcprog_t prognum;		/* Program number */
	rpcvers_t versnum;		/* Version number */
	const struct netconfig *nconf; /* Netconfig structure for the network */
{
	SVCXPRT *xprt;

	if (nconf == NULL) {
		warnx(
	"svc_tp_create: invalid netconfig structure for prog %u vers %u",
				(unsigned)prognum, (unsigned)versnum);
		return (NULL);
	}
	xprt = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0);
	if (xprt == NULL) {
		return (NULL);
	}
	(void) rpcb_unset(prognum, versnum, __UNCONST(nconf));
	if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) {
		warnx(
		"svc_tp_create: Could not register prog %u vers %u on %s",
				(unsigned)prognum, (unsigned)versnum,
				nconf->nc_netid);
		SVC_DESTROY(xprt);
		return (NULL);
	}
	return (xprt);
}

/*
 * If fd is RPC_ANYFD, then it opens a fd for the given transport
 * provider (nconf cannot be NULL then). If the t_state is T_UNBND and
 * bindaddr is NON-NULL, it performs a t_bind using the bindaddr. For
 * NULL bindadr and Connection oriented transports, the value of qlen
 * is set to 8.
 *
 * If sendsz or recvsz are zero, their default values are chosen.
 */
SVCXPRT *
svc_tli_create(fd, nconf, bindaddr, sendsz, recvsz)
	int fd;				/* Connection end point */
	const struct netconfig *nconf;	/* Netconfig struct for nettoken */
	const struct t_bind *bindaddr;	/* Local bind address */
	u_int sendsz;			/* Max sendsize */
	u_int recvsz;			/* Max recvsize */
{
	SVCXPRT *xprt = NULL;		/* service handle */
	bool_t madefd = FALSE;		/* whether fd opened here  */
	struct __rpc_sockinfo si;
	struct sockaddr_storage ss;
	socklen_t slen;

	if (fd == RPC_ANYFD) {
		if (nconf == NULL) {
			warnx("svc_tli_create: invalid netconfig");
			return (NULL);
		}
		fd = __rpc_nconf2fd(nconf);
		if (fd == -1) {
			warnx(
			    "svc_tli_create: could not open connection for %s",
					nconf->nc_netid);
			return (NULL);
		}
		__rpc_nconf2sockinfo(nconf, &si);
		madefd = TRUE;
	} else {
		/*
		 * It is an open descriptor. Get the transport info.
		 */
		if (!__rpc_fd2sockinfo(fd, &si)) {
			warnx(
		"svc_tli_create: could not get transport information");
			return (NULL);
		}
	}

	/*
	 * If the fd is unbound, try to bind it.
	 */
	if (madefd || !__rpc_sockisbound(fd)) {
		if (bindaddr == NULL) {
			if (bindresvport(fd, NULL) < 0) {
				memset(&ss, 0, sizeof ss);
				ss.ss_family = si.si_af;
				ss.ss_len = si.si_alen;
				if (bind(fd, (struct sockaddr *)(void *)&ss,
				    (socklen_t)si.si_alen) < 0) {
					warnx(
			"svc_tli_create: could not bind to anonymous port");
					goto freedata;
				}
			}
			listen(fd, SOMAXCONN);
		} else {
			if (bind(fd,
			    (struct sockaddr *)bindaddr->addr.buf,
			    (socklen_t)si.si_alen) < 0) {
				warnx(
		"svc_tli_create: could not bind to requested address");
				goto freedata;
			}
			listen(fd, (int)bindaddr->qlen);
		}
			
	}
	/*
	 * call transport specific function.
	 */
	switch (si.si_socktype) {
		case SOCK_STREAM:
			slen = sizeof ss;
			if (getpeername(fd, (struct sockaddr *)(void *)&ss, &slen)
			    == 0) {
				/* accepted socket */
				xprt = svc_fd_create(fd, sendsz, recvsz);
			} else
				xprt = svc_vc_create(fd, sendsz, recvsz);
			if (!nconf || !xprt)
				break;
#if 0
			/* XXX fvdl */
			if (strcmp(nconf->nc_protofmly, "inet") == 0 ||
			    strcmp(nconf->nc_protofmly, "inet6") == 0)
				(void) __svc_vc_setflag(xprt, TRUE);
#endif
			break;
		case SOCK_DGRAM:
			xprt = svc_dg_create(fd, sendsz, recvsz);
			break;
		default:
			warnx("svc_tli_create: bad service type");
			goto freedata;
	}

	if (xprt == NULL)
		/*
		 * The error messages here are spitted out by the lower layers:
		 * svc_vc_create(), svc_fd_create() and svc_dg_create().
		 */
		goto freedata;

	/* Fill in type of service */
	xprt->xp_type = __rpc_socktype2seman(si.si_socktype);

	if (nconf) {
		xprt->xp_netid = strdup(nconf->nc_netid);
		xprt->xp_tp = strdup(nconf->nc_device);
		if (xprt->xp_netid == NULL || xprt->xp_tp == NULL) {
			svc_destroy(xprt);
			return NULL;
		}
	}
	return (xprt);

freedata:
	if (madefd)
		(void) rump_sys_close(fd);
	return (NULL);
}

File Added: src/tests/fs/common/nfsrpc/Attic/svc_run.c
/*	$NetBSD: svc_run.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char *sccsid = "@(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro";
static char *sccsid = "@(#)svc_run.c	2.1 88/07/29 4.0 RPCSRC";
#else
__RCSID("$NetBSD: svc_run.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * This is the rpc server side idle loop
 * Wait for input, call server program.
 */
#include "namespace.h"
#include "reentrant.h"
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#include <rpc/rpc.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#include "rpc_internal.h"
#include "svc_fdset.h"

void
svc_run()
{
	fd_set readfds, cleanfds;
	int maxfd;
	int probs = 0;
	struct timeval timeout;
#ifdef _REENTRANT
	extern rwlock_t svc_fd_lock;
#endif

	timeout.tv_sec = 30;
	timeout.tv_usec = 0;

	for (;;) {
		rwlock_rdlock(&svc_fd_lock);
		readfds = *get_fdset();
		cleanfds = *get_fdset();
		maxfd = *get_fdsetmax();
		rwlock_unlock(&svc_fd_lock);
		switch (rump_sys_select(maxfd+1,
		    &readfds, NULL, NULL, &timeout)) {
		case -1:
			if ((errno == EINTR || errno == EBADF) && probs < 100) {
				probs++;
				continue;
			}
			warn("svc_run: - select failed");
			return;
		case 0:
			__svc_clean_idle(&cleanfds, 30, FALSE);
			continue;
		default:
			svc_getreqset(&readfds);
			probs = 0;
		}
	}
}

/*
 *      This function causes svc_run() to exit by telling it that it has no
 *      more work to do.
 */
void
svc_exit()
{
#ifdef _REENTRANT
	extern rwlock_t svc_fd_lock;
#endif

	rwlock_wrlock(&svc_fd_lock);
	FD_ZERO(get_fdset());
	rwlock_unlock(&svc_fd_lock);
}

File Added: src/tests/fs/common/nfsrpc/Attic/svc_vc.c
/*	$NetBSD: svc_vc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char *sccsid = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
static char *sccsid = "@(#)svc_tcp.c	2.2 88/08/01 4.0 RPCSRC";
#else
__RCSID("$NetBSD: svc_vc.c,v 1.1 2010/07/26 15:56:45 pooka Exp $");
#endif
#endif

/*
 * svc_vc.c, Server side for Connection Oriented based RPC. 
 *
 * Actually implements two flavors of transporter -
 * a tcp rendezvouser (a listner and connection establisher)
 * and a record/tcp stream.
 */

#include "namespace.h"
#include "reentrant.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <netinet/in.h>

#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <rpc/rpc.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>

#include "rpc_internal.h"
#include "svc_fdset.h"

#ifdef _REENTRANT
extern rwlock_t svc_fd_lock;
#endif

static SVCXPRT *makefd_xprt __P((int, u_int, u_int));
static bool_t rendezvous_request __P((SVCXPRT *, struct rpc_msg *));
static enum xprt_stat rendezvous_stat __P((SVCXPRT *));
static void svc_vc_destroy __P((SVCXPRT *));
static void __svc_vc_dodestroy __P((SVCXPRT *));
static int read_vc __P((caddr_t, caddr_t, int));
static int write_vc __P((caddr_t, caddr_t, int));
static enum xprt_stat svc_vc_stat __P((SVCXPRT *));
static bool_t svc_vc_recv __P((SVCXPRT *, struct rpc_msg *));
static bool_t svc_vc_getargs __P((SVCXPRT *, xdrproc_t, caddr_t));
static bool_t svc_vc_freeargs __P((SVCXPRT *, xdrproc_t, caddr_t));
static bool_t svc_vc_reply __P((SVCXPRT *, struct rpc_msg *));
static void svc_vc_rendezvous_ops __P((SVCXPRT *));
static void svc_vc_ops __P((SVCXPRT *));
static bool_t svc_vc_control __P((SVCXPRT *, const u_int, void *));
static bool_t svc_vc_rendezvous_control __P((SVCXPRT *, const u_int,
					     void *));

struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */
	u_int sendsize;
	u_int recvsize;
	int maxrec;
};

struct cf_conn {  /* kept in xprt->xp_p1 for actual connection */
	enum xprt_stat strm_stat;
	u_int32_t x_id;
	XDR xdrs;
	char verf_body[MAX_AUTH_BYTES];
	u_int sendsize;
	u_int recvsize;
	int maxrec;
	bool_t nonblock;
	struct timeval last_recv_time;
};

/*
 * Usage:
 *	xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
 *
 * Creates, registers, and returns a (rpc) tcp based transporter.
 * Once *xprt is initialized, it is registered as a transporter
 * see (svc.h, xprt_register).  This routine returns
 * a NULL if a problem occurred.
 *
 * The filedescriptor passed in is expected to refer to a bound, but
 * not yet connected socket.
 *
 * Since streams do buffered io similar to stdio, the caller can specify
 * how big the send and receive buffers are via the second and third parms;
 * 0 => use the system default.
 */
SVCXPRT *
svc_vc_create(fd, sendsize, recvsize)
	int fd;
	u_int sendsize;
	u_int recvsize;
{
	SVCXPRT *xprt;
	struct cf_rendezvous *r = NULL;
	struct __rpc_sockinfo si;
	struct sockaddr_storage sslocal;
	socklen_t slen;
	int one = 1;

	if (!__rpc_fd2sockinfo(fd, &si))
		return NULL;

	r = mem_alloc(sizeof(*r));
	if (r == NULL) {
		warnx("svc_vc_create: out of memory");
		return NULL;
	}
	r->sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
	r->recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
	r->maxrec = __svc_maxrec;
	xprt = mem_alloc(sizeof(SVCXPRT));
	if (xprt == NULL) {
		warnx("svc_vc_create: out of memory");
		goto cleanup_svc_vc_create;
	}
	xprt->xp_tp = NULL;
	xprt->xp_p1 = (caddr_t)(void *)r;
	xprt->xp_p2 = NULL;
	xprt->xp_p3 = NULL;
	xprt->xp_verf = _null_auth;
	svc_vc_rendezvous_ops(xprt);
	xprt->xp_port = (u_short)-1;	/* It is the rendezvouser */
	xprt->xp_fd = fd;

	slen = sizeof (struct sockaddr_storage);
	if (getsockname(fd, (struct sockaddr *)(void *)&sslocal, &slen) < 0) {
		warnx("svc_vc_create: could not retrieve local addr");
		goto cleanup_svc_vc_create;
	}

	/*
	 * We want to be able to check credentials on local sockets.
	 */
	if (sslocal.ss_family == AF_LOCAL)
		if (setsockopt(fd, 0, LOCAL_CREDS, &one, sizeof one) < 0)
			goto cleanup_svc_vc_create;

	xprt->xp_ltaddr.maxlen = xprt->xp_ltaddr.len = sslocal.ss_len;
	xprt->xp_ltaddr.buf = mem_alloc((size_t)sslocal.ss_len);
	if (xprt->xp_ltaddr.buf == NULL) {
		warnx("svc_vc_create: no mem for local addr");
		goto cleanup_svc_vc_create;
	}
	memcpy(xprt->xp_ltaddr.buf, &sslocal, (size_t)sslocal.ss_len);

	xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
	xprt_register(xprt);
	return (xprt);
cleanup_svc_vc_create:
	if (xprt)
		mem_free(xprt, sizeof(*xprt));
	if (r != NULL)
		mem_free(r, sizeof(*r));
	return (NULL);
}

/*
 * Like svtcp_create(), except the routine takes any *open* UNIX file
 * descriptor as its first input.
 */
SVCXPRT *
svc_fd_create(fd, sendsize, recvsize)
	int fd;
	u_int sendsize;
	u_int recvsize;
{
	struct sockaddr_storage ss;
	socklen_t slen;
	SVCXPRT *ret;

	_DIAGASSERT(fd != -1);

	ret = makefd_xprt(fd, sendsize, recvsize);
	if (ret == NULL)
		return NULL;

	slen = sizeof (struct sockaddr_storage);
	if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
		warnx("svc_fd_create: could not retrieve local addr");
		goto freedata;
	}
	ret->xp_ltaddr.maxlen = ret->xp_ltaddr.len = ss.ss_len;
	ret->xp_ltaddr.buf = mem_alloc((size_t)ss.ss_len);
	if (ret->xp_ltaddr.buf == NULL) {
		warnx("svc_fd_create: no mem for local addr");
		goto freedata;
	}
	memcpy(ret->xp_ltaddr.buf, &ss, (size_t)ss.ss_len);

	slen = sizeof (struct sockaddr_storage);
	if (getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) {
		warnx("svc_fd_create: could not retrieve remote addr");
		goto freedata;
	}
	ret->xp_rtaddr.maxlen = ret->xp_rtaddr.len = ss.ss_len;
	ret->xp_rtaddr.buf = mem_alloc((size_t)ss.ss_len);
	if (ret->xp_rtaddr.buf == NULL) {
		warnx("svc_fd_create: no mem for local addr");
		goto freedata;
	}
	memcpy(ret->xp_rtaddr.buf, &ss, (size_t)ss.ss_len);
#ifdef PORTMAP
	if (ss.ss_family == AF_INET) {
		ret->xp_raddr = *(struct sockaddr_in *)ret->xp_rtaddr.buf;
		ret->xp_addrlen = sizeof (struct sockaddr_in);
	}
#endif

	return ret;

freedata:
	if (ret->xp_ltaddr.buf != NULL)
		mem_free(ret->xp_ltaddr.buf, rep->xp_ltaddr.maxlen);

	return NULL;
}

static SVCXPRT *
makefd_xprt(fd, sendsize, recvsize)
	int fd;
	u_int sendsize;
	u_int recvsize;
{
	SVCXPRT *xprt;
	struct cf_conn *cd;
	const char *netid;
	struct __rpc_sockinfo si;
 
	_DIAGASSERT(fd != -1);

	xprt = mem_alloc(sizeof(SVCXPRT));
	if (xprt == NULL)
		goto out;
	memset(xprt, 0, sizeof *xprt);
	cd = mem_alloc(sizeof(struct cf_conn));
	if (cd == NULL)
		goto out;
	cd->strm_stat = XPRT_IDLE;
	xdrrec_create(&(cd->xdrs), sendsize, recvsize,
	    (caddr_t)(void *)xprt, read_vc, write_vc);
	xprt->xp_p1 = (caddr_t)(void *)cd;
	xprt->xp_verf.oa_base = cd->verf_body;
	svc_vc_ops(xprt);  /* truely deals with calls */
	xprt->xp_port = 0;  /* this is a connection, not a rendezvouser */
	xprt->xp_fd = fd;
	if (__rpc_fd2sockinfo(fd, &si) && __rpc_sockinfo2netid(&si, &netid))
		if ((xprt->xp_netid = strdup(netid)) == NULL)
			goto out;

	xprt_register(xprt);
	return (xprt);
out:
	warn("svc_tcp: makefd_xprt");
	if (xprt)
		mem_free(xprt, sizeof(SVCXPRT));
	return NULL;
}

/*ARGSUSED*/
static bool_t
rendezvous_request(xprt, msg)
	SVCXPRT *xprt;
	struct rpc_msg *msg;
{
	int sock, flags;
	struct cf_rendezvous *r;
	struct cf_conn *cd;
	struct sockaddr_storage addr;
	socklen_t len;
	struct __rpc_sockinfo si;
	SVCXPRT *newxprt;
	fd_set cleanfds;

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(msg != NULL);

	r = (struct cf_rendezvous *)xprt->xp_p1;
again:
	len = sizeof addr;
	if ((sock = accept(xprt->xp_fd, (struct sockaddr *)(void *)&addr,
	    &len)) < 0) {
		if (errno == EINTR)
			goto again;
		/*
		 * Clean out the most idle file descriptor when we're
		 * running out.
		 */
		if (errno == EMFILE || errno == ENFILE) {
			cleanfds = *get_fdset();
			if (__svc_clean_idle(&cleanfds, 0, FALSE))
				goto again;
		}
		return (FALSE);
	}
	/*
	 * make a new transporter (re-uses xprt)
	 */
	newxprt = makefd_xprt(sock, r->sendsize, r->recvsize);
	if (newxprt == NULL)
		goto out;
	newxprt->xp_rtaddr.buf = mem_alloc(len);
	if (newxprt->xp_rtaddr.buf == NULL)
		goto out;
	memcpy(newxprt->xp_rtaddr.buf, &addr, len);
	newxprt->xp_rtaddr.len = len;
#ifdef PORTMAP
	if (addr.ss_family == AF_INET) {
		newxprt->xp_raddr = *(struct sockaddr_in *)newxprt->xp_rtaddr.buf;
		newxprt->xp_addrlen = sizeof (struct sockaddr_in);
	}
#endif
	if (__rpc_fd2sockinfo(sock, &si))
		__rpc_setnodelay(sock, &si);

	cd = (struct cf_conn *)newxprt->xp_p1;

	cd->recvsize = r->recvsize;
	cd->sendsize = r->sendsize;
	cd->maxrec = r->maxrec;

	if (cd->maxrec != 0) {
		flags = rump_sys_fcntl(sock, F_GETFL, (void *)0);
		if (flags  == -1)
			goto out;
		if (rump_sys_fcntl(sock, F_SETFL, (void *)(flags | O_NONBLOCK)) == -1)
			goto out;
		if (cd->recvsize > cd->maxrec)
			cd->recvsize = cd->maxrec;
		cd->nonblock = TRUE;
		__xdrrec_setnonblock(&cd->xdrs, cd->maxrec);
	} else
		cd->nonblock = FALSE;

	(void)gettimeofday(&cd->last_recv_time, NULL);

	return (FALSE); /* there is never an rpc msg to be processed */
out:
	(void)rump_sys_close(sock);
	return (FALSE); /* there was an error */
}

/*ARGSUSED*/
static enum xprt_stat
rendezvous_stat(xprt)
	SVCXPRT *xprt;
{

	return (XPRT_IDLE);
}

static void
svc_vc_destroy(xprt)
	SVCXPRT *xprt;
{
	_DIAGASSERT(xprt != NULL);

	xprt_unregister(xprt);
	__svc_vc_dodestroy(xprt);
}

static void
__svc_vc_dodestroy(xprt)
	SVCXPRT *xprt;
{
	struct cf_conn *cd;
	struct cf_rendezvous *r;

	cd = (struct cf_conn *)xprt->xp_p1;

	if (xprt->xp_fd != RPC_ANYFD)
		(void)rump_sys_close(xprt->xp_fd);
	if (xprt->xp_port != 0) {
		/* a rendezvouser socket */
		r = (struct cf_rendezvous *)xprt->xp_p1;
		mem_free(r, sizeof (struct cf_rendezvous));
		xprt->xp_port = 0;
	} else {
		/* an actual connection socket */
		XDR_DESTROY(&(cd->xdrs));
		mem_free(cd, sizeof(struct cf_conn));
	}
	if (xprt->xp_rtaddr.buf)
		mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
	if (xprt->xp_ltaddr.buf)
		mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
	if (xprt->xp_tp)
		free(xprt->xp_tp);
	if (xprt->xp_netid)
		free(xprt->xp_netid);
	mem_free(xprt, sizeof(SVCXPRT));
}

/*ARGSUSED*/
static bool_t
svc_vc_control(xprt, rq, in)
	SVCXPRT *xprt;
	const u_int rq;
	void *in;
{
	return (FALSE);
}

/*ARGSUSED*/
static bool_t
svc_vc_rendezvous_control(xprt, rq, in)
	SVCXPRT *xprt;
	const u_int rq;
	void *in;
{
	struct cf_rendezvous *cfp;

	cfp = (struct cf_rendezvous *)xprt->xp_p1;
	if (cfp == NULL)
		return (FALSE);
	switch (rq) {
		case SVCGET_CONNMAXREC:
			*(int *)in = cfp->maxrec;
			break;
		case SVCSET_CONNMAXREC:
			cfp->maxrec = *(int *)in;
			break;
		default:
			return (FALSE);
	}
	return (TRUE);
}

/*
 * reads data from the tcp connection.
 * any error is fatal and the connection is closed.
 * (And a read of zero bytes is a half closed stream => error.)
 * All read operations timeout after 35 seconds.  A timeout is
 * fatal for the connection.
 */
static int
read_vc(xprtp, buf, len)
	caddr_t xprtp;
	caddr_t buf;
	int len;
{
	SVCXPRT *xprt;
	int sock;
	struct pollfd pollfd;
	struct sockaddr *sa;
	struct msghdr msg;
	struct cmsghdr *cmp;
	void *crmsg = NULL;
	struct sockcred *sc;
	socklen_t crmsgsize;
	struct cf_conn *cfp;
	static const struct timespec ts = { 35, 0 };

	xprt = (SVCXPRT *)(void *)xprtp;
	_DIAGASSERT(xprt != NULL);

	sock = xprt->xp_fd;

	sa = (struct sockaddr *)xprt->xp_rtaddr.buf;
	if (sa->sa_family == AF_LOCAL && xprt->xp_p2 == NULL) {
		memset(&msg, 0, sizeof msg);
		crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
		crmsg = malloc(crmsgsize);
		if (crmsg == NULL)
			goto fatal_err;
		memset(crmsg, 0, crmsgsize);

		msg.msg_control = crmsg;
		msg.msg_controllen = crmsgsize;

		if (recvmsg(sock, &msg, 0) < 0)
			goto fatal_err;

		if (msg.msg_controllen == 0 ||
		    (msg.msg_flags & MSG_CTRUNC) != 0)
			goto fatal_err;

		cmp = CMSG_FIRSTHDR(&msg);
		if (cmp->cmsg_level != SOL_SOCKET ||
		    cmp->cmsg_type != SCM_CREDS)
			goto fatal_err;

		sc = (struct sockcred *)(void *)CMSG_DATA(cmp);

		xprt->xp_p2 = mem_alloc(SOCKCREDSIZE(sc->sc_ngroups));
		if (xprt->xp_p2 == NULL)
			goto fatal_err;

		memcpy(xprt->xp_p2, sc, SOCKCREDSIZE(sc->sc_ngroups));
		free(crmsg);
		crmsg = NULL;
	}

	cfp = (struct cf_conn *)xprt->xp_p1;

	if (cfp->nonblock) {
		len = rump_sys_read(sock, buf, (size_t)len);
		if (len < 0) {
			if (errno == EAGAIN)
				len = 0;
			else
				goto fatal_err;
		}
		if (len != 0)
			gettimeofday(&cfp->last_recv_time, NULL);
		return len;
	}

	do {
		pollfd.fd = sock;
		pollfd.events = POLLIN;
		switch (rump_sys_pollts(&pollfd, 1, &ts, NULL)) {
		case -1:
			if (errno == EINTR) {
				continue;
			}
			/*FALLTHROUGH*/
		case 0:
			goto fatal_err;

		default:
			break;
		}
	} while ((pollfd.revents & POLLIN) == 0);

	if ((len = rump_sys_read(sock, buf, (size_t)len)) > 0) {
		gettimeofday(&cfp->last_recv_time, NULL);
		return (len);
	}

fatal_err:
	if (crmsg != NULL)
		free(crmsg);
	((struct cf_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
	return (-1);
}

/*
 * writes data to the tcp connection.
 * Any error is fatal and the connection is closed.
 */
static int
write_vc(xprtp, buf, len)
	caddr_t xprtp;
	caddr_t buf;
	int len;
{
	SVCXPRT *xprt;
	int i, cnt;
	struct cf_conn *cd;
	struct timeval tv0, tv1;

	xprt = (SVCXPRT *)(void *)xprtp;
	_DIAGASSERT(xprt != NULL);

	cd = (struct cf_conn *)xprt->xp_p1;

	if (cd->nonblock)
		gettimeofday(&tv0, NULL);

	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
		if ((i = rump_sys_write(xprt->xp_fd, buf, (size_t)cnt)) < 0) {
			if (errno != EAGAIN || !cd->nonblock) {
				cd->strm_stat = XPRT_DIED;
				return (-1);
			}
			if (cd->nonblock && i != cnt) {
				/*
				 * For non-blocking connections, do not
				 * take more than 2 seconds writing the
				 * data out.
				 *
				 * XXX 2 is an arbitrary amount.
				 */
				gettimeofday(&tv1, NULL);
				if (tv1.tv_sec - tv0.tv_sec >= 2) {
					cd->strm_stat = XPRT_DIED;
					return (-1);
				}
			}
		}
	}
	return (len);
}

static enum xprt_stat
svc_vc_stat(xprt)
	SVCXPRT *xprt;
{
	struct cf_conn *cd;

	_DIAGASSERT(xprt != NULL);

	cd = (struct cf_conn *)(xprt->xp_p1);

	if (cd->strm_stat == XPRT_DIED)
		return (XPRT_DIED);
	if (! xdrrec_eof(&(cd->xdrs)))
		return (XPRT_MOREREQS);
	return (XPRT_IDLE);
}

static bool_t
svc_vc_recv(xprt, msg)
	SVCXPRT *xprt;
	struct rpc_msg *msg;
{
	struct cf_conn *cd;
	XDR *xdrs;

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(msg != NULL);

	cd = (struct cf_conn *)(xprt->xp_p1);
	xdrs = &(cd->xdrs);

	if (cd->nonblock) {
		if (!__xdrrec_getrec(xdrs, &cd->strm_stat, TRUE))
			return FALSE;
	}

	xdrs->x_op = XDR_DECODE;
	(void)xdrrec_skiprecord(xdrs);

	if (xdr_callmsg(xdrs, msg)) {
		cd->x_id = msg->rm_xid;
		return (TRUE);
	}
	cd->strm_stat = XPRT_DIED;
	return (FALSE);
}

static bool_t
svc_vc_getargs(xprt, xdr_args, args_ptr)
	SVCXPRT *xprt;
	xdrproc_t xdr_args;
	caddr_t args_ptr;
{

	_DIAGASSERT(xprt != NULL);
	/* args_ptr may be NULL */

	return ((*xdr_args)(&(((struct cf_conn *)(xprt->xp_p1))->xdrs),
	    args_ptr));
}

static bool_t
svc_vc_freeargs(xprt, xdr_args, args_ptr)
	SVCXPRT *xprt;
	xdrproc_t xdr_args;
	caddr_t args_ptr;
{
	XDR *xdrs;

	_DIAGASSERT(xprt != NULL);
	/* args_ptr may be NULL */

	xdrs = &(((struct cf_conn *)(xprt->xp_p1))->xdrs);

	xdrs->x_op = XDR_FREE;
	return ((*xdr_args)(xdrs, args_ptr));
}

static bool_t
svc_vc_reply(xprt, msg)
	SVCXPRT *xprt;
	struct rpc_msg *msg;
{
	struct cf_conn *cd;
	XDR *xdrs;
	bool_t rstat;

	_DIAGASSERT(xprt != NULL);
	_DIAGASSERT(msg != NULL);

	cd = (struct cf_conn *)(xprt->xp_p1);
	xdrs = &(cd->xdrs);

	xdrs->x_op = XDR_ENCODE;
	msg->rm_xid = cd->x_id;
	rstat = xdr_replymsg(xdrs, msg);
	(void)xdrrec_endofrecord(xdrs, TRUE);
	return (rstat);
}

static void
svc_vc_ops(xprt)
	SVCXPRT *xprt;
{
	static struct xp_ops ops;
	static struct xp_ops2 ops2;
#ifdef _REENTRANT
	extern mutex_t ops_lock;
#endif

/* VARIABLES PROTECTED BY ops_lock: ops, ops2 */

	mutex_lock(&ops_lock);
	if (ops.xp_recv == NULL) {
		ops.xp_recv = svc_vc_recv;
		ops.xp_stat = svc_vc_stat;
		ops.xp_getargs = svc_vc_getargs;
		ops.xp_reply = svc_vc_reply;
		ops.xp_freeargs = svc_vc_freeargs;
		ops.xp_destroy = svc_vc_destroy;
		ops2.xp_control = svc_vc_control;
	}
	xprt->xp_ops = &ops;
	xprt->xp_ops2 = &ops2;
	mutex_unlock(&ops_lock);
}

static void
svc_vc_rendezvous_ops(xprt)
	SVCXPRT *xprt;
{
	static struct xp_ops ops;
	static struct xp_ops2 ops2;
#ifdef _REENTRANT
	extern mutex_t ops_lock;
#endif
/* XXXGCC vax compiler unhappy otherwise */
#ifdef __vax__     
extern void abort(void);
#endif

	mutex_lock(&ops_lock);
	if (ops.xp_recv == NULL) {
		ops.xp_recv = rendezvous_request;
		ops.xp_stat = rendezvous_stat;
		ops.xp_getargs =
		    (bool_t (*) __P((SVCXPRT *, xdrproc_t, caddr_t)))abort;
		ops.xp_reply =
		    (bool_t (*) __P((SVCXPRT *, struct rpc_msg *)))abort;
		ops.xp_freeargs =
		    (bool_t (*) __P((SVCXPRT *, xdrproc_t, caddr_t)))abort;
		ops.xp_destroy = svc_vc_destroy;
		ops2.xp_control = svc_vc_rendezvous_control;
	}
	xprt->xp_ops = &ops;
	xprt->xp_ops2 = &ops2;
	mutex_unlock(&ops_lock);
}

/*
 * Destroy xprts that have not have had any activity in 'timeout' seconds.
 * If 'cleanblock' is true, blocking connections (the default) are also
 * cleaned. If timeout is 0, the least active connection is picked.
 */
bool_t
__svc_clean_idle(fd_set *fds, int timeout, bool_t cleanblock)
{
	int i, ncleaned;
	SVCXPRT *xprt, *least_active;
	struct timeval tv, tdiff, tmax;
	struct cf_conn *cd;

	gettimeofday(&tv, NULL);
	tmax.tv_sec = tmax.tv_usec = 0;
	least_active = NULL;
	rwlock_wrlock(&svc_fd_lock);
	for (i = ncleaned = 0; i <= svc_maxfd; i++) {
		if (FD_ISSET(i, fds)) {
			xprt = __svc_xports[i];
			if (xprt == NULL || xprt->xp_ops == NULL ||
			    xprt->xp_ops->xp_recv != svc_vc_recv)
				continue;
			cd = (struct cf_conn *)xprt->xp_p1;
			if (!cleanblock && !cd->nonblock)
				continue;
			if (timeout == 0) {
				timersub(&tv, &cd->last_recv_time, &tdiff);
				if (timercmp(&tdiff, &tmax, >)) {
					tmax = tdiff;
					least_active = xprt;
				}
				continue;
			}
			if (tv.tv_sec - cd->last_recv_time.tv_sec > timeout) {
				__xprt_unregister_unlocked(xprt);
				__svc_vc_dodestroy(xprt);
				ncleaned++;
			}
		}
	}
	if (timeout == 0 && least_active != NULL) {
		__xprt_unregister_unlocked(least_active);
		__svc_vc_dodestroy(least_active);
		ncleaned++;
	}
	rwlock_unlock(&svc_fd_lock);
	return ncleaned > 0 ? TRUE : FALSE;
}