2ab850aedfef47e23e7831db2f8286f62074bd47
[games.git] / lib / libc / rpc / pmap_rmt.c
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  *
29  * @(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro
30  * @(#)pmap_rmt.c       2.2 88/08/01 4.0 RPCSRC
31  * $FreeBSD: src/lib/libc/rpc/pmap_rmt.c,v 1.16.2.1 2002/06/30 23:34:58 iedowse Exp $
32  * $DragonFly: src/lib/libc/rpc/pmap_rmt.c,v 1.4 2005/01/31 22:29:38 dillon Exp $
33  */
34
35 /*
36  * pmap_rmt.c
37  * Client interface to pmap rpc service.
38  * remote call and broadcast service
39  *
40  * Copyright (C) 1984, Sun Microsystems, Inc.
41  */
42
43 #include "namespace.h"
44 #include <rpc/rpc.h>
45 #include <rpc/pmap_prot.h>
46 #include <rpc/pmap_clnt.h>
47 #include <rpc/pmap_rmt.h>
48 #include <sys/socket.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <errno.h>
53 #include <string.h>
54 #include <net/if.h>
55 #include <sys/ioctl.h>
56 #include <arpa/inet.h>
57 #include "un-namespace.h"
58
59 #define MAX_BROADCAST_SIZE 1400
60
61 static struct timeval timeout = { 3, 0 };
62
63 /*
64  * pmapper remote-call-service interface.
65  * This routine is used to call the pmapper remote call service
66  * which will look up a service program in the port maps, and then
67  * remotely call that routine with the given parameters.  This allows
68  * programs to do a lookup and call in one step.
69 */
70 enum clnt_stat
71 pmap_rmtcall(addr, prog, vers, proc, xdrargs, argsp, xdrres, resp, tout, port_ptr)
72         struct sockaddr_in *addr;
73         u_long prog, vers, proc;
74         xdrproc_t xdrargs, xdrres;
75         caddr_t argsp, resp;
76         struct timeval tout;
77         u_long *port_ptr;
78 {
79         int socket = -1;
80         CLIENT *client;
81         struct rmtcallargs a;
82         struct rmtcallres r;
83         enum clnt_stat stat;
84
85         addr->sin_port = htons(PMAPPORT);
86         client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &socket);
87         if (client != (CLIENT *)NULL) {
88                 a.prog = prog;
89                 a.vers = vers;
90                 a.proc = proc;
91                 a.args_ptr = argsp;
92                 a.xdr_args = xdrargs;
93                 r.port_ptr = port_ptr;
94                 r.results_ptr = resp;
95                 r.xdr_results = xdrres;
96                 stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a,
97                     xdr_rmtcallres, &r, tout);
98                 CLNT_DESTROY(client);
99         } else {
100                 stat = RPC_FAILED;
101         }
102         if (socket != -1)
103                 (void)_close(socket);
104         addr->sin_port = 0;
105         return (stat);
106 }
107
108
109 /*
110  * XDR remote call arguments
111  * written for XDR_ENCODE direction only
112  */
113 bool_t
114 xdr_rmtcall_args(xdrs, cap)
115         XDR *xdrs;
116         struct rmtcallargs *cap;
117 {
118         u_int lenposition, argposition, position;
119
120         if (xdr_u_long(xdrs, &(cap->prog)) &&
121             xdr_u_long(xdrs, &(cap->vers)) &&
122             xdr_u_long(xdrs, &(cap->proc))) {
123                 lenposition = XDR_GETPOS(xdrs);
124                 if (! xdr_u_long(xdrs, &(cap->arglen)))
125                     return (FALSE);
126                 argposition = XDR_GETPOS(xdrs);
127                 if (! (*(cap->xdr_args))(xdrs, cap->args_ptr))
128                     return (FALSE);
129                 position = XDR_GETPOS(xdrs);
130                 cap->arglen = (u_long)position - (u_long)argposition;
131                 XDR_SETPOS(xdrs, lenposition);
132                 if (! xdr_u_long(xdrs, &(cap->arglen)))
133                     return (FALSE);
134                 XDR_SETPOS(xdrs, position);
135                 return (TRUE);
136         }
137         return (FALSE);
138 }
139
140 /*
141  * XDR remote call results
142  * written for XDR_DECODE direction only
143  */
144 bool_t
145 xdr_rmtcallres(xdrs, crp)
146         XDR *xdrs;
147         struct rmtcallres *crp;
148 {
149         caddr_t port_ptr;
150
151         port_ptr = (caddr_t)crp->port_ptr;
152         if (xdr_reference(xdrs, &port_ptr, sizeof (u_long),
153             xdr_u_long) && xdr_u_long(xdrs, &crp->resultslen)) {
154                 crp->port_ptr = (u_long *)port_ptr;
155                 return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
156         }
157         return (FALSE);
158 }
159
160
161 /*
162  * The following is kludged-up support for simple rpc broadcasts.
163  * Someday a large, complicated system will replace these trivial
164  * routines which only support udp/ip .
165  */
166
167 static int
168 getbroadcastnets(addrs, sock, buf)
169         struct in_addr *addrs;
170         int sock;  /* any valid socket will do */
171         char *buf;  /* why allocxate more when we can use existing... */
172 {
173         struct ifconf ifc;
174         struct ifreq ifreq, *ifr;
175         struct sockaddr_in *sin;
176         struct  in_addr addr;
177         char *cp, *cplim;
178         int n, i = 0;
179
180         ifc.ifc_len = UDPMSGSIZE;
181         ifc.ifc_buf = buf;
182         if (_ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
183                 perror("broadcast: ioctl (get interface configuration)");
184                 return (0);
185         }
186 #define max(a, b) (a > b ? a : b)
187 #define size(p) max((p).sa_len, sizeof(p))
188         cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
189         for (cp = buf; cp < cplim;
190                         cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
191                 ifr = (struct ifreq *)cp;
192                 if (ifr->ifr_addr.sa_family != AF_INET)
193                         continue;
194                 memcpy(&ifreq, ifr, sizeof(ifreq));
195                 if (_ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
196                         perror("broadcast: ioctl (get interface flags)");
197                         continue;
198                 }
199                 if ((ifreq.ifr_flags & IFF_BROADCAST) &&
200                     (ifreq.ifr_flags & IFF_UP)) {
201                         sin = (struct sockaddr_in *)&ifr->ifr_addr;
202 #ifdef SIOCGIFBRDADDR   /* 4.3BSD */
203                         if (_ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
204                                 addr =
205                                     inet_makeaddr(inet_netof(sin->sin_addr),
206                                     INADDR_ANY);
207                         } else {
208                                 addr = ((struct sockaddr_in*)
209                                   &ifreq.ifr_addr)->sin_addr;
210                         }
211 #else /* 4.2 BSD */
212                         addr = inet_makeaddr(inet_netof(sin->sin_addr),
213                             INADDR_ANY);
214 #endif
215                         for (n=i-1; n>=0; n--) {
216                                 if (addr.s_addr == addrs[n].s_addr)
217                                         break;
218                         }
219                         if (n<0) {
220                                 addrs[i++] = addr;
221                         }
222                 }
223         }
224         return (i);
225 }
226
227 typedef bool_t (*resultproc_t)();
228
229 enum clnt_stat
230 clnt_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, eachresult)
231         u_long          prog;           /* program number */
232         u_long          vers;           /* version number */
233         u_long          proc;           /* procedure number */
234         xdrproc_t       xargs;          /* xdr routine for args */
235         caddr_t         argsp;          /* pointer to args */
236         xdrproc_t       xresults;       /* xdr routine for results */
237         caddr_t         resultsp;       /* pointer to results */
238         resultproc_t    eachresult;     /* call with each result obtained */
239 {
240         enum clnt_stat stat;
241         AUTH *unix_auth = authunix_create_default();
242         XDR xdr_stream;
243         XDR *xdrs = &xdr_stream;
244         int outlen, inlen, fromlen, nets;
245         int sock;
246         int on = 1;
247         fd_set *fds, readfds;
248         int i;
249         bool_t done = FALSE;
250         u_long xid;
251         u_long port;
252         struct in_addr addrs[20];
253         struct sockaddr_in baddr, raddr; /* broadcast and response addresses */
254         struct rmtcallargs a;
255         struct rmtcallres r;
256         struct rpc_msg msg;
257         struct timeval t, tv;
258         char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE];
259         static u_int32_t disrupt;
260
261         if (disrupt == 0)
262                 disrupt = (u_int32_t)(long)resultsp;
263
264         /*
265          * initialization: create a socket, a broadcast address, and
266          * preserialize the arguments into a send buffer.
267          */
268         if ((sock = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
269                 perror("Cannot create socket for broadcast rpc");
270                 stat = RPC_CANTSEND;
271                 goto done_broad;
272         }
273 #ifdef SO_BROADCAST
274         if (_setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
275                 perror("Cannot set socket option SO_BROADCAST");
276                 stat = RPC_CANTSEND;
277                 goto done_broad;
278         }
279 #endif /* def SO_BROADCAST */
280         if (sock + 1 > FD_SETSIZE) {
281                 int bytes = howmany(sock + 1, NFDBITS) * sizeof(fd_mask);
282                 fds = (fd_set *)malloc(bytes);
283                 if (fds == NULL) {
284                         stat = RPC_CANTSEND;
285                         goto done_broad;
286                 }
287                 memset(fds, 0, bytes);
288         } else {
289                 fds = &readfds;
290                 FD_ZERO(fds);
291         }
292
293         nets = getbroadcastnets(addrs, sock, inbuf);
294         memset(&baddr, 0, sizeof (baddr));
295         baddr.sin_len = sizeof(struct sockaddr_in);
296         baddr.sin_family = AF_INET;
297         baddr.sin_port = htons(PMAPPORT);
298         baddr.sin_addr.s_addr = htonl(INADDR_ANY);
299         (void)gettimeofday(&t, (struct timezone *)0);
300         msg.rm_xid = xid = (++disrupt) ^ getpid() ^ t.tv_sec ^ t.tv_usec;
301         t.tv_usec = 0;
302         msg.rm_direction = CALL;
303         msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
304         msg.rm_call.cb_prog = PMAPPROG;
305         msg.rm_call.cb_vers = PMAPVERS;
306         msg.rm_call.cb_proc = PMAPPROC_CALLIT;
307         msg.rm_call.cb_cred = unix_auth->ah_cred;
308         msg.rm_call.cb_verf = unix_auth->ah_verf;
309         a.prog = prog;
310         a.vers = vers;
311         a.proc = proc;
312         a.xdr_args = xargs;
313         a.args_ptr = argsp;
314         r.port_ptr = &port;
315         r.xdr_results = xresults;
316         r.results_ptr = resultsp;
317         xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE);
318         if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) {
319                 stat = RPC_CANTENCODEARGS;
320                 goto done_broad;
321         }
322         outlen = (int)xdr_getpos(xdrs);
323         xdr_destroy(xdrs);
324         /*
325          * Basic loop: broadcast a packet and wait a while for response(s).
326          * The response timeout grows larger per iteration.
327          *
328          * XXX This will loop about 5 times the stop. If there are
329          * lots of signals being received by the process it will quit
330          * send them all in one quick burst, not paying attention to
331          * the intended function of sending them slowly over half a
332          * minute or so
333          */
334         for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) {
335                 int success = 0;
336                 for (i = 0; i < nets; i++) {
337                         baddr.sin_addr = addrs[i];
338                         if (_sendto(sock, outbuf, outlen, 0,
339                                 (struct sockaddr *)&baddr,
340                                 sizeof (struct sockaddr)) == outlen) {
341                                 success++;
342                         }
343                 }
344                 if (!success) {
345                         perror("Cannot send broadcast packet");
346                         stat = RPC_CANTSEND;
347                         goto done_broad;
348                 }
349                 if (eachresult == NULL) {
350                         stat = RPC_SUCCESS;
351                         goto done_broad;
352                 }
353         recv_again:
354                 msg.acpted_rply.ar_verf = _null_auth;
355                 msg.acpted_rply.ar_results.where = (caddr_t)&r;
356                 msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
357                 /* XXX we know the other bits are still clear */
358                 FD_SET(sock, fds);
359                 tv = t;         /* for _select() that copies back */
360                 switch (_select(sock + 1, fds, NULL, NULL, &tv)) {
361
362                 case 0:  /* timed out */
363                         stat = RPC_TIMEDOUT;
364                         continue;
365
366                 case -1:  /* some kind of error */
367                         if (errno == EINTR)
368                                 goto recv_again;
369                         perror("Broadcast select problem");
370                         stat = RPC_CANTRECV;
371                         goto done_broad;
372
373                 }  /* end of select results switch */
374         try_again:
375                 fromlen = sizeof(struct sockaddr);
376                 inlen = _recvfrom(sock, inbuf, UDPMSGSIZE, 0,
377                         (struct sockaddr *)&raddr, &fromlen);
378                 if (inlen < 0) {
379                         if (errno == EINTR)
380                                 goto try_again;
381                         perror("Cannot receive reply to broadcast");
382                         stat = RPC_CANTRECV;
383                         goto done_broad;
384                 }
385                 if (inlen < sizeof(u_int32_t))
386                         goto recv_again;
387                 /*
388                  * see if reply transaction id matches sent id.
389                  * If so, decode the results.
390                  */
391                 xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE);
392                 if (xdr_replymsg(xdrs, &msg)) {
393                         if ((msg.rm_xid == xid) &&
394                                 (msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
395                                 (msg.acpted_rply.ar_stat == SUCCESS)) {
396                                 raddr.sin_port = htons((u_short)port);
397                                 done = (*eachresult)(resultsp, &raddr);
398                         }
399                         /* otherwise, we just ignore the errors ... */
400                 }
401                 xdrs->x_op = XDR_FREE;
402                 msg.acpted_rply.ar_results.proc = xdr_void;
403                 (void)xdr_replymsg(xdrs, &msg);
404                 (void)(*xresults)(xdrs, resultsp);
405                 xdr_destroy(xdrs);
406                 if (done) {
407                         stat = RPC_SUCCESS;
408                         goto done_broad;
409                 } else {
410                         goto recv_again;
411                 }
412         }
413 done_broad:
414         if (fds != &readfds)
415                 free(fds);
416         if (sock >= 0)
417                 (void)_close(sock);
418         AUTH_DESTROY(unix_auth);
419         return (stat);
420 }
421