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
# $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
/* $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);
}
/* $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);
}
/* $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);
}
/* $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);
}
/* $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);
}
/* $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_ */
/* $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 */
/* $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));
}
/* $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;
/* $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 */
/* $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);
}
/* $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;
}
/* $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);
}
/* $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)
/* $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);
/* $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);
}
/* $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);
}
/* $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;
}