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