Path: blob/main/crypto/krb5/src/lib/rpc/pmap_rmt.c
39537 views
/* @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC */1/*2* Copyright (c) 2010, Oracle America, Inc.3*4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions are met:8*9* * Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* * Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in14* the documentation and/or other materials provided with the15* distribution.16*17* * Neither the name of the "Oracle America, Inc." nor the names of18* its contributors may be used to endorse or promote products19* derived from this software without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS22* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED23* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A24* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT25* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,26* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED27* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR28* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF29* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING30* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS31* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.32*/33#if !defined(lint) && defined(SCCSIDS)34static char sccsid[] = "@(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro";35#endif3637/*38* pmap_rmt.c39* Client interface to pmap rpc service.40* remote call and broadcast service41*/4243#include "k5-platform.h"44#include <unistd.h>45#include <gssrpc/rpc.h>46#include <gssrpc/pmap_prot.h>47#include <gssrpc/pmap_clnt.h>48#include <gssrpc/pmap_rmt.h>49#include <sys/socket.h>50#ifdef sun51#include <sys/sockio.h>52#endif53#ifdef OSF154#include <net/route.h>55#include <sys/mbuf.h>56#endif57#include <net/if.h>58#include <sys/ioctl.h>59#include <arpa/inet.h>60#define MAX_BROADCAST_SIZE 140061#include <port-sockets.h>62#include "socket-utils.h"6364static struct timeval timeout = { 3, 0 };6566#ifndef GETSOCKNAME_ARG3_TYPE67#define GETSOCKNAME_ARG3_TYPE int68#endif6970/*71* pmapper remote-call-service interface.72* This routine is used to call the pmapper remote call service73* which will look up a service program in the port maps, and then74* remotely call that routine with the given parameters. This allows75* programs to do a lookup and call in one step.76*/77enum clnt_stat78pmap_rmtcall(79struct sockaddr_in *addr,80rpcprog_t prog,81rpcvers_t vers,82rpcproc_t proc,83xdrproc_t xdrargs,84caddr_t argsp,85xdrproc_t xdrres,86caddr_t resp,87struct timeval tout,88rpcport_t *port_ptr)89{90SOCKET sock = INVALID_SOCKET;91CLIENT *client;92struct rmtcallargs a;93struct rmtcallres r;94enum clnt_stat stat;9596addr->sin_port = htons(PMAPPORT);97client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &sock);98if (client != (CLIENT *)NULL) {99a.prog = prog;100a.vers = vers;101a.proc = proc;102a.args_ptr = argsp;103a.xdr_args = xdrargs;104r.port_ptr = port_ptr;105r.results_ptr = resp;106r.xdr_results = xdrres;107stat = CLNT_CALL(client, PMAPPROC_CALLIT,108(xdrproc_t)xdr_rmtcall_args, &a,109(xdrproc_t)xdr_rmtcallres, &r, tout);110CLNT_DESTROY(client);111} else {112stat = RPC_FAILED;113}114(void)closesocket(sock);115addr->sin_port = 0;116return (stat);117}118119120/*121* XDR remote call arguments122* written for XDR_ENCODE direction only123*/124bool_t125xdr_rmtcall_args(126XDR *xdrs,127struct rmtcallargs *cap)128{129u_int lenposition, argposition, position;130131if (xdr_u_int32(xdrs, &(cap->prog)) &&132xdr_u_int32(xdrs, &(cap->vers)) &&133xdr_u_int32(xdrs, &(cap->proc))) {134lenposition = XDR_GETPOS(xdrs);135if (! xdr_u_int32(xdrs, &(cap->arglen)))136return (FALSE);137argposition = XDR_GETPOS(xdrs);138if (! (*(cap->xdr_args))(xdrs, cap->args_ptr))139return (FALSE);140position = XDR_GETPOS(xdrs);141cap->arglen = (uint32_t)position - (uint32_t)argposition;142XDR_SETPOS(xdrs, lenposition);143if (! xdr_u_int32(xdrs, &(cap->arglen)))144return (FALSE);145XDR_SETPOS(xdrs, position);146return (TRUE);147}148return (FALSE);149}150151/*152* XDR remote call results153* written for XDR_DECODE direction only154*/155bool_t156xdr_rmtcallres(157XDR *xdrs,158struct rmtcallres *crp)159{160caddr_t port_ptr;161162port_ptr = (caddr_t)(void *)crp->port_ptr;163if (!xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),164(xdrproc_t)xdr_u_int32))165return (FALSE);166crp->port_ptr = (uint32_t *)(void *)port_ptr;167if (xdr_u_int32(xdrs, &crp->resultslen))168return ((*(crp->xdr_results))(xdrs, crp->results_ptr));169return (FALSE);170}171172173/*174* The following is kludged-up support for simple rpc broadcasts.175* Someday a large, complicated system will replace these trivial176* routines which only support udp/ip .177*/178179#define GIFCONF_BUFSIZE (256 * sizeof (struct ifconf))180181static int182getbroadcastnets(183struct in_addr *addrs,184int sock, /* any valid socket will do */185char *buf /* why allocxate more when we can use existing... */186)187{188struct ifconf ifc;189struct ifreq ifreq, *ifr;190int n, i;191192ifc.ifc_len = GIFCONF_BUFSIZE;193ifc.ifc_buf = buf;194memset (buf, 0, GIFCONF_BUFSIZE);195if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {196perror("broadcast: ioctl (get interface configuration)");197return (0);198}199ifr = ifc.ifc_req;200for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {201ifreq = *ifr;202if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {203perror("broadcast: ioctl (get interface flags)");204continue;205}206if ((ifreq.ifr_flags & IFF_BROADCAST) &&207(ifreq.ifr_flags & IFF_UP) &&208ifr->ifr_addr.sa_family == AF_INET) {209#ifdef SIOCGIFBRDADDR /* 4.3BSD */210if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {211addrs[i++].s_addr = INADDR_ANY;212} else {213addrs[i++] = sa2sin(&ifreq.ifr_addr)->sin_addr;214}215#else /* 4.2 BSD */216struct sockaddr_in *sockin;217sockin = sa2sin(&ifr->ifr_addr);218addrs[i++] = inet_makeaddr(inet_netof219(sockin->sin_addr.s_addr), INADDR_ANY);220#endif221}222}223return (i);224}225226enum clnt_stat227clnt_broadcast(228rpcprog_t prog, /* program number */229rpcvers_t vers, /* version number */230rpcproc_t proc, /* procedure number */231xdrproc_t xargs, /* xdr routine for args */232caddr_t argsp, /* pointer to args */233xdrproc_t xresults, /* xdr routine for results */234caddr_t resultsp, /* pointer to results */235resultproc_t eachresult /* call with each result obtained */236)237{238enum clnt_stat stat;239AUTH *unix_auth = authunix_create_default();240XDR xdr_stream;241XDR *xdrs = &xdr_stream;242int outlen, nets;243ssize_t inlen;244GETSOCKNAME_ARG3_TYPE fromlen;245SOCKET sock;246int on = 1;247#ifdef FD_SETSIZE248fd_set mask;249fd_set readfds;250#else251int readfds;252int mask;253#endif /* def FD_SETSIZE */254int i;255bool_t done = FALSE;256uint32_t xid;257rpcport_t port;258struct in_addr addrs[20];259struct sockaddr_in baddr, raddr; /* broadcast and response addresses */260struct rmtcallargs a;261struct rmtcallres r;262struct rpc_msg msg;263struct timeval t, t2;264char outbuf[MAX_BROADCAST_SIZE];265#ifndef MAX266#define MAX(A,B) ((A)<(B)?(B):(A))267#endif268char inbuf[MAX (UDPMSGSIZE, GIFCONF_BUFSIZE)];269270/*271* initialization: create a socket, a broadcast address, and272* preserialize the arguments into a send buffer.273*/274if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {275perror("Cannot create socket for broadcast rpc");276stat = RPC_CANTSEND;277goto done_broad;278}279set_cloexec_fd(sock);280#ifdef SO_BROADCAST281if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &on,282sizeof (on)) < 0) {283perror("Cannot set socket option SO_BROADCAST");284stat = RPC_CANTSEND;285goto done_broad;286}287#endif /* def SO_BROADCAST */288#ifdef FD_SETSIZE289FD_ZERO(&mask);290FD_SET(sock, &mask);291#else292mask = (1 << sock);293#endif /* def FD_SETSIZE */294nets = getbroadcastnets(addrs, sock, inbuf);295memset(&baddr, 0, sizeof (baddr));296baddr.sin_family = AF_INET;297baddr.sin_port = htons(PMAPPORT);298baddr.sin_addr.s_addr = htonl(INADDR_ANY);299/* baddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); */300(void)gettimeofday(&t, (struct timezone *)0);301msg.rm_xid = xid = getpid() ^ t.tv_sec ^ t.tv_usec;302t.tv_usec = 0;303msg.rm_direction = CALL;304msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;305msg.rm_call.cb_prog = PMAPPROG;306msg.rm_call.cb_vers = PMAPVERS;307msg.rm_call.cb_proc = PMAPPROC_CALLIT;308msg.rm_call.cb_cred = unix_auth->ah_cred;309msg.rm_call.cb_verf = unix_auth->ah_verf;310a.prog = prog;311a.vers = vers;312a.proc = proc;313a.xdr_args = xargs;314a.args_ptr = argsp;315r.port_ptr = &port;316r.xdr_results = xresults;317r.results_ptr = resultsp;318xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE);319if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) {320stat = RPC_CANTENCODEARGS;321goto done_broad;322}323outlen = (int)xdr_getpos(xdrs);324xdr_destroy(xdrs);325/*326* Basic loop: broadcast a packet and wait a while for response(s).327* The response timeout grows larger per iteration.328*/329for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) {330for (i = 0; i < nets; i++) {331baddr.sin_addr = addrs[i];332if (sendto(sock, outbuf, outlen, 0,333(struct sockaddr *)&baddr,334sizeof (struct sockaddr)) != outlen) {335perror("Cannot send broadcast packet");336stat = RPC_CANTSEND;337goto done_broad;338}339}340if (eachresult == NULL) {341stat = RPC_SUCCESS;342goto done_broad;343}344recv_again:345msg.acpted_rply.ar_verf = gssrpc__null_auth;346msg.acpted_rply.ar_results.where = (caddr_t)&r;347msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres;348readfds = mask;349t2 = t;350switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set *)NULL,351(fd_set *)NULL, &t2)) {352353case 0: /* timed out */354stat = RPC_TIMEDOUT;355continue;356357case -1: /* some kind of error */358if (errno == EINTR)359goto recv_again;360perror("Broadcast select problem");361stat = RPC_CANTRECV;362goto done_broad;363364} /* end of select results switch */365try_again:366fromlen = sizeof(struct sockaddr);367inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0,368(struct sockaddr *)&raddr, &fromlen);369if (inlen < 0) {370if (errno == EINTR)371goto try_again;372perror("Cannot receive reply to broadcast");373stat = RPC_CANTRECV;374goto done_broad;375}376if ((size_t)inlen < sizeof(uint32_t))377goto recv_again;378/*379* see if reply transaction id matches sent id.380* If so, decode the results.381*/382xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE);383if (xdr_replymsg(xdrs, &msg)) {384if ((msg.rm_xid == xid) &&385(msg.rm_reply.rp_stat == MSG_ACCEPTED) &&386(msg.acpted_rply.ar_stat == SUCCESS)) {387raddr.sin_port = htons((u_short)port);388done = (*eachresult)(resultsp, &raddr);389}390/* otherwise, we just ignore the errors ... */391} else {392#ifdef notdef393/* some kind of deserialization problem ... */394if (msg.rm_xid == xid)395fprintf(stderr, "Broadcast deserialization problem");396/* otherwise, just random garbage */397#endif398}399xdrs->x_op = XDR_FREE;400msg.acpted_rply.ar_results.proc = xdr_void;401(void)xdr_replymsg(xdrs, &msg);402(void)(*xresults)(xdrs, resultsp);403xdr_destroy(xdrs);404if (done) {405stat = RPC_SUCCESS;406goto done_broad;407} else {408goto recv_again;409}410}411done_broad:412(void)closesocket(sock);413AUTH_DESTROY(unix_auth);414return (stat);415}416417418