#if defined(LIBC_SCCS) && !defined(lint)
static char *rcsid = "$Id: pmap_rmt.c,v 1.6 2004/12/19 22:45:44 zarzycki Exp $";
#endif
#include <string.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_rmt.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#define MAX_BROADCAST_SIZE 1400
static struct timeval timeout = { 3, 0 };
enum clnt_stat
pmap_rmtcall(addr, prog, vers, proc, xdrargs, argsp, xdrres, resp, tout, port_ptr)
#ifdef __LP64__
struct sockaddr_in *addr;
uint32_t prog, vers, proc;
xdrproc_t xdrargs, xdrres;
caddr_t argsp, resp;
struct timeval tout;
uint32_t *port_ptr;
#else
struct sockaddr_in *addr;
u_long prog, vers, proc;
xdrproc_t xdrargs, xdrres;
caddr_t argsp, resp;
struct timeval tout;
u_long *port_ptr;
#endif
{
int socket = -1;
register CLIENT *client;
struct rmtcallargs a;
struct rmtcallres r;
enum clnt_stat stat;
addr->sin_port = htons(PMAPPORT);
client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &socket);
if (client != (CLIENT *)NULL) {
a.prog = prog;
a.vers = vers;
a.proc = proc;
a.args_ptr = argsp;
a.xdr_args = xdrargs;
r.port_ptr = port_ptr;
r.results_ptr = resp;
r.xdr_results = xdrres;
stat = CLNT_CALL(client, PMAPPROC_CALLIT, (xdrproc_t)xdr_rmtcall_args, &a, (xdrproc_t)xdr_rmtcallres, &r, tout);
CLNT_DESTROY(client);
} else {
stat = RPC_FAILED;
}
(void)close(socket);
addr->sin_port = 0;
return (stat);
}
bool_t
xdr_rmtcall_args(xdrs, cap)
register XDR *xdrs;
register struct rmtcallargs *cap;
{
u_int lenposition, argposition, position;
if (xdr_u_long(xdrs, &(cap->prog)) &&
xdr_u_long(xdrs, &(cap->vers)) &&
xdr_u_long(xdrs, &(cap->proc))) {
lenposition = XDR_GETPOS(xdrs);
if (! xdr_u_long(xdrs, &(cap->arglen)))
return (FALSE);
argposition = XDR_GETPOS(xdrs);
if (! (*(cap->xdr_args))(xdrs, cap->args_ptr, 0))
return (FALSE);
position = XDR_GETPOS(xdrs);
#ifdef __LP64__
cap->arglen = (uint32_t)position - (uint32_t)argposition;
#else
cap->arglen = (u_long)position - (u_long)argposition;
#endif
XDR_SETPOS(xdrs, lenposition);
if (! xdr_u_long(xdrs, &(cap->arglen)))
return (FALSE);
XDR_SETPOS(xdrs, position);
return (TRUE);
}
return (FALSE);
}
bool_t
xdr_rmtcallres(xdrs, crp)
register XDR *xdrs;
register struct rmtcallres *crp;
{
caddr_t port_ptr;
port_ptr = (caddr_t)crp->port_ptr;
#ifdef __LP64__
if (xdr_reference(xdrs, &port_ptr, sizeof (uint32_t), (xdrproc_t)xdr_u_long) && xdr_u_long(xdrs, &crp->resultslen)) {
crp->port_ptr = (unsigned int *)port_ptr;
return ((*(crp->xdr_results))(xdrs, crp->results_ptr, 0));
}
#else
if (xdr_reference(xdrs, &port_ptr, sizeof (u_long), (xdrproc_t)xdr_u_long) && xdr_u_long(xdrs, &crp->resultslen)) {
crp->port_ptr = (unsigned long *)port_ptr;
return ((*(crp->xdr_results))(xdrs, crp->results_ptr, 0));
}
#endif
return (FALSE);
}
static int
getbroadcastnets(addrs, sock, buf)
struct in_addr *addrs;
int sock;
char *buf;
{
struct ifconf ifc;
struct ifreq ifreq, *ifr;
struct sockaddr_in *sin;
char *cp, *cplim;
int i = 0;
ifc.ifc_len = UDPMSGSIZE;
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
perror("broadcast: ioctl (get interface configuration)");
return (0);
}
#define max(a, b) (a > b ? a : b)
#define size(p) max((p).sa_len, sizeof(p))
cplim = buf + ifc.ifc_len;
for (cp = buf; cp < cplim;
cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
ifr = (struct ifreq *)cp;
if (ifr->ifr_addr.sa_family != AF_INET)
continue;
ifreq = *ifr;
if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
perror("broadcast: ioctl (get interface flags)");
continue;
}
if ((ifreq.ifr_flags & IFF_BROADCAST) &&
(ifreq.ifr_flags & IFF_UP)) {
sin = (struct sockaddr_in *)&ifr->ifr_addr;
#ifdef SIOCGIFBRDADDR
if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
addrs[i++] =
inet_makeaddr(inet_netof(sin->sin_addr),
INADDR_ANY);
} else {
addrs[i++] = ((struct sockaddr_in*)
&ifreq.ifr_addr)->sin_addr;
}
#else
addrs[i++] = inet_makeaddr(inet_netof(sin->sin_addr),
INADDR_ANY);
#endif
}
}
return (i);
}
typedef bool_t (*resultproc_t)();
enum clnt_stat
clnt_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, eachresult)
#ifdef __LP64__
uint32_t prog;
uint32_t vers;
uint32_t proc;
xdrproc_t xargs;
caddr_t argsp;
xdrproc_t xresults;
caddr_t resultsp;
resultproc_t eachresult;
#else
u_long prog;
u_long vers;
u_long proc;
xdrproc_t xargs;
caddr_t argsp;
xdrproc_t xresults;
caddr_t resultsp;
resultproc_t eachresult;
#endif
{
enum clnt_stat stat;
AUTH *unix_auth = authunix_create_default();
XDR xdr_stream;
register XDR *xdrs = &xdr_stream;
int outlen, inlen, nets;
unsigned int fromlen;
register int sock;
int on = 1;
fd_set mask;
fd_set readfds;
register int i;
bool_t done = FALSE;
uint32_t xid;
#ifdef __LP64__
unsigned int port;
#else
unsigned long port;
#endif
struct in_addr addrs[20];
struct sockaddr_in baddr, raddr;
struct rmtcallargs a;
struct rmtcallres r;
struct rpc_msg msg;
struct timeval t;
char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE];
int rfd;
stat = RPC_SUCCESS;
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("Cannot create socket for broadcast rpc");
stat = RPC_CANTSEND;
goto done_broad;
}
#ifdef SO_BROADCAST
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
perror("Cannot set socket option SO_BROADCAST");
stat = RPC_CANTSEND;
goto done_broad;
}
#endif
FD_ZERO(&mask);
FD_SET(sock, &mask);
nets = getbroadcastnets(addrs, sock, inbuf);
bzero((char *)&baddr, sizeof (baddr));
baddr.sin_family = AF_INET;
baddr.sin_port = htons(PMAPPORT);
baddr.sin_addr.s_addr = htonl(INADDR_ANY);
rfd = open("/dev/random", O_RDONLY, 0);
if ((rfd < 0) || (read(rfd, &xid, sizeof(xid)) != sizeof(xid)))
{
gettimeofday(&t, (struct timezone *)0);
xid = getpid() ^ t.tv_sec ^ t.tv_usec;
}
if (rfd > 0) close(rfd);
t.tv_usec = 0;
msg.rm_xid = xid;
msg.rm_direction = CALL;
msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
msg.rm_call.cb_prog = PMAPPROG;
msg.rm_call.cb_vers = PMAPVERS;
msg.rm_call.cb_proc = PMAPPROC_CALLIT;
msg.rm_call.cb_cred = unix_auth->ah_cred;
msg.rm_call.cb_verf = unix_auth->ah_verf;
a.prog = prog;
a.vers = vers;
a.proc = proc;
a.xdr_args = xargs;
a.args_ptr = argsp;
r.port_ptr = &port;
r.xdr_results = xresults;
r.results_ptr = resultsp;
xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE);
if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) {
stat = RPC_CANTENCODEARGS;
goto done_broad;
}
outlen = (int)xdr_getpos(xdrs);
xdr_destroy(xdrs);
for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) {
for (i = 0; i < nets; i++) {
baddr.sin_addr = addrs[i];
if (sendto(sock, outbuf, outlen, 0,
(struct sockaddr *)&baddr,
sizeof (struct sockaddr)) != outlen) {
perror("Cannot send broadcast packet");
stat = RPC_CANTSEND;
goto done_broad;
}
}
if (eachresult == NULL) {
stat = RPC_SUCCESS;
goto done_broad;
}
recv_again:
msg.acpted_rply.ar_verf = _null_auth;
msg.acpted_rply.ar_results.where = (caddr_t)&r;
msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres;
readfds = mask;
switch (select(sock+1, &readfds, NULL, NULL, &t)) {
case 0:
stat = RPC_TIMEDOUT;
continue;
case -1:
if (errno == EINTR)
goto recv_again;
perror("Broadcast select problem");
stat = RPC_CANTRECV;
goto done_broad;
}
try_again:
fromlen = sizeof(struct sockaddr);
inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, (struct sockaddr *)&raddr, &fromlen);
if (inlen < 0) {
if (errno == EINTR)
goto try_again;
perror("Cannot receive reply to broadcast");
stat = RPC_CANTRECV;
goto done_broad;
}
#ifdef __LP64__
if (inlen < sizeof(uint32_t))
goto recv_again;
#else
if (inlen < sizeof(u_long))
goto recv_again;
#endif
xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE);
if (xdr_replymsg(xdrs, &msg)) {
if ((msg.rm_xid == xid) &&
(msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
(msg.acpted_rply.ar_stat == SUCCESS)) {
raddr.sin_port = htons((u_short)port);
done = (*eachresult)(resultsp, &raddr);
}
} else {
#ifdef notdef
if (msg.rm_xid == xid)
fprintf(stderr, "Broadcast deserialization problem");
#endif
}
xdrs->x_op = XDR_FREE;
msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
(void)xdr_replymsg(xdrs, &msg);
(void)(*xresults)(xdrs, resultsp, 0);
xdr_destroy(xdrs);
if (done) {
stat = RPC_SUCCESS;
goto done_broad;
} else {
goto recv_again;
}
}
done_broad:
(void)close(sock);
AUTH_DESTROY(unix_auth);
return (stat);
}