Update.
[jlayton/glibc.git] / sunrpc / clnt_udp.c
1 /* @(#)clnt_udp.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 !defined(lint) && defined(SCCSIDS)
31 static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
32 #endif
33
34 /*
35  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
36  *
37  * Copyright (C) 1984, Sun Microsystems, Inc.
38  */
39
40 #include <stdio.h>
41 #include <unistd.h>
42 #include <rpc/rpc.h>
43 #include <rpc/xdr.h>
44 #include <rpc/clnt.h>
45 #include <sys/poll.h>
46 #include <sys/socket.h>
47 #include <sys/ioctl.h>
48 #include <netdb.h>
49 #include <errno.h>
50 #include <rpc/pmap_clnt.h>
51 #include <net/if.h>
52
53 extern bool_t xdr_opaque_auth (XDR *, struct opaque_auth *);
54 extern u_long _create_xid (void);
55
56 /*
57  * UDP bases client side rpc operations
58  */
59 static enum clnt_stat clntudp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
60                                     xdrproc_t, caddr_t, struct timeval);
61 static void clntudp_abort (void);
62 static void clntudp_geterr (CLIENT *, struct rpc_err *);
63 static bool_t clntudp_freeres (CLIENT *, xdrproc_t, caddr_t);
64 static bool_t clntudp_control (CLIENT *, int, char *);
65 static void clntudp_destroy (CLIENT *);
66
67 static struct clnt_ops udp_ops =
68 {
69   clntudp_call,
70   clntudp_abort,
71   clntudp_geterr,
72   clntudp_freeres,
73   clntudp_destroy,
74   clntudp_control
75 };
76
77 /*
78  * Private data kept per client handle
79  */
80 struct cu_data
81   {
82     int cu_sock;
83     bool_t cu_closeit;
84     struct sockaddr_in cu_raddr;
85     int cu_rlen;
86     struct timeval cu_wait;
87     struct timeval cu_total;
88     struct rpc_err cu_error;
89     XDR cu_outxdrs;
90     u_int cu_xdrpos;
91     u_int cu_sendsz;
92     char *cu_outbuf;
93     u_int cu_recvsz;
94     char cu_inbuf[1];
95   };
96
97 /*
98  * Create a UDP based client handle.
99  * If *sockp<0, *sockp is set to a newly created UPD socket.
100  * If raddr->sin_port is 0 a binder on the remote machine
101  * is consulted for the correct port number.
102  * NB: It is the clients responsibility to close *sockp.
103  * NB: The rpch->cl_auth is initialized to null authentication.
104  *     Caller may wish to set this something more useful.
105  *
106  * wait is the amount of time used between retransmitting a call if
107  * no response has been heard; retransmission occurs until the actual
108  * rpc call times out.
109  *
110  * sendsz and recvsz are the maximum allowable packet sizes that can be
111  * sent and received.
112  */
113 CLIENT *
114 clntudp_bufcreate (struct sockaddr_in *raddr, u_long program, u_long version,
115                    struct timeval wait, int *sockp, u_int sendsz,
116                    u_int recvsz)
117 {
118   CLIENT *cl;
119   struct cu_data *cu = NULL;
120   struct rpc_msg call_msg;
121
122   cl = (CLIENT *) mem_alloc (sizeof (CLIENT));
123   if (cl == NULL)
124     {
125       (void) fprintf (stderr, _("clntudp_create: out of memory\n"));
126       rpc_createerr.cf_stat = RPC_SYSTEMERROR;
127       rpc_createerr.cf_error.re_errno = errno;
128       goto fooy;
129     }
130   sendsz = ((sendsz + 3) / 4) * 4;
131   recvsz = ((recvsz + 3) / 4) * 4;
132   cu = (struct cu_data *) mem_alloc (sizeof (*cu) + sendsz + recvsz);
133   if (cu == NULL)
134     {
135       (void) fprintf (stderr, _("clntudp_create: out of memory\n"));
136       rpc_createerr.cf_stat = RPC_SYSTEMERROR;
137       rpc_createerr.cf_error.re_errno = errno;
138       goto fooy;
139     }
140   cu->cu_outbuf = &cu->cu_inbuf[recvsz];
141
142   if (raddr->sin_port == 0)
143     {
144       u_short port;
145       if ((port =
146            pmap_getport (raddr, program, version, IPPROTO_UDP)) == 0)
147         {
148           goto fooy;
149         }
150       raddr->sin_port = htons (port);
151     }
152   cl->cl_ops = &udp_ops;
153   cl->cl_private = (caddr_t) cu;
154   cu->cu_raddr = *raddr;
155   cu->cu_rlen = sizeof (cu->cu_raddr);
156   cu->cu_wait = wait;
157   cu->cu_total.tv_sec = -1;
158   cu->cu_total.tv_usec = -1;
159   cu->cu_sendsz = sendsz;
160   cu->cu_recvsz = recvsz;
161   call_msg.rm_xid = _create_xid ();
162   call_msg.rm_direction = CALL;
163   call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
164   call_msg.rm_call.cb_prog = program;
165   call_msg.rm_call.cb_vers = version;
166   xdrmem_create (&(cu->cu_outxdrs), cu->cu_outbuf,
167                  sendsz, XDR_ENCODE);
168   if (!xdr_callhdr (&(cu->cu_outxdrs), &call_msg))
169     {
170       goto fooy;
171     }
172   cu->cu_xdrpos = XDR_GETPOS (&(cu->cu_outxdrs));
173   if (*sockp < 0)
174     {
175       int dontblock = 1;
176
177       *sockp = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
178       if (*sockp < 0)
179         {
180           rpc_createerr.cf_stat = RPC_SYSTEMERROR;
181           rpc_createerr.cf_error.re_errno = errno;
182           goto fooy;
183         }
184       /* attempt to bind to prov port */
185       (void) bindresvport (*sockp, (struct sockaddr_in *) 0);
186       /* the sockets rpc controls are non-blocking */
187       (void) __ioctl (*sockp, FIONBIO, (char *) &dontblock);
188       cu->cu_closeit = TRUE;
189     }
190   else
191     {
192       cu->cu_closeit = FALSE;
193     }
194   cu->cu_sock = *sockp;
195   cl->cl_auth = authnone_create ();
196   return cl;
197 fooy:
198   if (cu)
199     mem_free ((caddr_t) cu, sizeof (*cu) + sendsz + recvsz);
200   if (cl)
201     mem_free ((caddr_t) cl, sizeof (CLIENT));
202   return (CLIENT *) NULL;
203 }
204
205 CLIENT *
206 clntudp_create (raddr, program, version, wait, sockp)
207      struct sockaddr_in *raddr;
208      u_long program;
209      u_long version;
210      struct timeval wait;
211      int *sockp;
212 {
213
214   return clntudp_bufcreate (raddr, program, version, wait, sockp,
215                             UDPMSGSIZE, UDPMSGSIZE);
216 }
217
218 static int
219 is_network_up (int sock)
220 {
221   struct ifconf ifc;
222   char buf[UDPMSGSIZE];
223   struct ifreq ifreq, *ifr;
224   int n;
225
226   ifc.ifc_len = sizeof (buf);
227   ifc.ifc_buf = buf;
228   if (__ioctl(sock, SIOCGIFCONF, (char *) &ifc) == 0)
229     {
230       ifr = ifc.ifc_req;
231       for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++)
232         {
233           ifreq = *ifr;
234           if (__ioctl (sock, SIOCGIFFLAGS, (char *) &ifreq) < 0)
235             break;
236
237           if ((ifreq.ifr_flags & IFF_UP)
238               && ifr->ifr_addr.sa_family == AF_INET)
239             return 1;
240         }
241     }
242   return 0;
243 }
244
245 static enum clnt_stat
246 clntudp_call (cl, proc, xargs, argsp, xresults, resultsp, utimeout)
247      CLIENT *cl;        /* client handle */
248      u_long proc;               /* procedure number */
249      xdrproc_t xargs;           /* xdr routine for args */
250      caddr_t argsp;             /* pointer to args */
251      xdrproc_t xresults;        /* xdr routine for results */
252      caddr_t resultsp;          /* pointer to results */
253      struct timeval utimeout;   /* seconds to wait before giving up */
254 {
255   struct cu_data *cu = (struct cu_data *) cl->cl_private;
256   XDR *xdrs;
257   int outlen = 0;
258   int inlen;
259   socklen_t fromlen;
260   struct pollfd fd;
261   int milliseconds = (cu->cu_wait.tv_sec * 1000) +
262     (cu->cu_wait.tv_usec / 1000);
263   struct sockaddr_in from;
264   struct rpc_msg reply_msg;
265   XDR reply_xdrs;
266   struct timeval time_waited;
267   bool_t ok;
268   int nrefreshes = 2;           /* number of times to refresh cred */
269   struct timeval timeout;
270   int anyup;                    /* any network interface up */
271
272   if (cu->cu_total.tv_usec == -1)
273     {
274       timeout = utimeout;       /* use supplied timeout */
275     }
276   else
277     {
278       timeout = cu->cu_total;   /* use default timeout */
279     }
280
281   time_waited.tv_sec = 0;
282   time_waited.tv_usec = 0;
283 call_again:
284   xdrs = &(cu->cu_outxdrs);
285   if (xargs == NULL)
286     goto get_reply;
287   xdrs->x_op = XDR_ENCODE;
288   XDR_SETPOS (xdrs, cu->cu_xdrpos);
289   /*
290    * the transaction is the first thing in the out buffer
291    */
292   (*(u_short *) (cu->cu_outbuf))++;
293   if ((!XDR_PUTLONG (xdrs, (long *) &proc)) ||
294       (!AUTH_MARSHALL (cl->cl_auth, xdrs)) ||
295       (!(*xargs) (xdrs, argsp)))
296     return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
297   outlen = (int) XDR_GETPOS (xdrs);
298
299 send_again:
300   if (sendto (cu->cu_sock, cu->cu_outbuf, outlen, 0,
301               (struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen)
302       != outlen)
303     {
304       cu->cu_error.re_errno = errno;
305       return (cu->cu_error.re_status = RPC_CANTSEND);
306     }
307
308   /*
309    * Hack to provide rpc-based message passing
310    */
311   if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
312     {
313       return (cu->cu_error.re_status = RPC_TIMEDOUT);
314     }
315  get_reply:
316   /*
317    * sub-optimal code appears here because we have
318    * some clock time to spare while the packets are in flight.
319    * (We assume that this is actually only executed once.)
320    */
321   reply_msg.acpted_rply.ar_verf = _null_auth;
322   reply_msg.acpted_rply.ar_results.where = resultsp;
323   reply_msg.acpted_rply.ar_results.proc = xresults;
324   fd.fd = cu->cu_sock;
325   fd.events = POLLIN;
326   for (;;)
327     {
328       switch (__poll (&fd, 1, milliseconds))
329         {
330
331         case 0:
332           if (anyup == 0)
333             {
334               anyup = is_network_up (cu->cu_sock);
335               if (!anyup)
336                 return (cu->cu_error.re_status = RPC_CANTRECV);
337             }
338
339           time_waited.tv_sec += cu->cu_wait.tv_sec;
340           time_waited.tv_usec += cu->cu_wait.tv_usec;
341           while (time_waited.tv_usec >= 1000000)
342             {
343               time_waited.tv_sec++;
344               time_waited.tv_usec -= 1000000;
345             }
346           if ((time_waited.tv_sec < timeout.tv_sec) ||
347               ((time_waited.tv_sec == timeout.tv_sec) &&
348                (time_waited.tv_usec < timeout.tv_usec)))
349             goto send_again;
350           return (cu->cu_error.re_status = RPC_TIMEDOUT);
351
352           /*
353            * buggy in other cases because time_waited is not being
354            * updated.
355            */
356         case -1:
357           if (errno == EINTR)
358             continue;
359           cu->cu_error.re_errno = errno;
360           return (cu->cu_error.re_status = RPC_CANTRECV);
361         }
362       do
363         {
364           fromlen = sizeof (struct sockaddr);
365           inlen = recvfrom (cu->cu_sock, cu->cu_inbuf,
366                             (int) cu->cu_recvsz, 0,
367                             (struct sockaddr *) &from, &fromlen);
368         }
369       while (inlen < 0 && errno == EINTR);
370       if (inlen < 0)
371         {
372           if (errno == EWOULDBLOCK)
373             continue;
374           cu->cu_error.re_errno = errno;
375           return (cu->cu_error.re_status = RPC_CANTRECV);
376         }
377       if (inlen < 4)
378         continue;
379
380       /* see if reply transaction id matches sent id.
381         Don't do this if we only wait for a replay */
382       if (xargs != NULL
383           && (*((u_int32_t *) (cu->cu_inbuf))
384               != *((u_int32_t *) (cu->cu_outbuf))))
385         continue;
386       /* we now assume we have the proper reply */
387       break;
388     }
389
390   /*
391    * now decode and validate the response
392    */
393   xdrmem_create (&reply_xdrs, cu->cu_inbuf, (u_int) inlen, XDR_DECODE);
394   ok = xdr_replymsg (&reply_xdrs, &reply_msg);
395   /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
396   if (ok)
397     {
398       _seterr_reply (&reply_msg, &(cu->cu_error));
399       if (cu->cu_error.re_status == RPC_SUCCESS)
400         {
401           if (!AUTH_VALIDATE (cl->cl_auth,
402                               &reply_msg.acpted_rply.ar_verf))
403             {
404               cu->cu_error.re_status = RPC_AUTHERROR;
405               cu->cu_error.re_why = AUTH_INVALIDRESP;
406             }
407           if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
408             {
409               xdrs->x_op = XDR_FREE;
410               (void) xdr_opaque_auth (xdrs,
411                                       &(reply_msg.acpted_rply.ar_verf));
412             }
413         }                       /* end successful completion */
414       else
415         {
416           /* maybe our credentials need to be refreshed ... */
417           if (nrefreshes > 0 && AUTH_REFRESH (cl->cl_auth))
418             {
419               nrefreshes--;
420               goto call_again;
421             }
422         }                       /* end of unsuccessful completion */
423     }                           /* end of valid reply message */
424   else
425     {
426       cu->cu_error.re_status = RPC_CANTDECODERES;
427     }
428   return cu->cu_error.re_status;
429 }
430
431 static void
432 clntudp_geterr (CLIENT *cl, struct rpc_err *errp)
433 {
434   struct cu_data *cu = (struct cu_data *) cl->cl_private;
435
436   *errp = cu->cu_error;
437 }
438
439
440 static bool_t
441 clntudp_freeres (CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
442 {
443   struct cu_data *cu = (struct cu_data *) cl->cl_private;
444   XDR *xdrs = &(cu->cu_outxdrs);
445
446   xdrs->x_op = XDR_FREE;
447   return (*xdr_res) (xdrs, res_ptr);
448 }
449
450 static void
451 clntudp_abort (void)
452 {
453 }
454
455 static bool_t
456 clntudp_control (CLIENT *cl, int request, char *info)
457 {
458   struct cu_data *cu = (struct cu_data *) cl->cl_private;
459
460   switch (request)
461     {
462     case CLSET_FD_CLOSE:
463       cu->cu_closeit = TRUE;
464       break;
465     case CLSET_FD_NCLOSE:
466       cu->cu_closeit = FALSE;
467       break;
468     case CLSET_TIMEOUT:
469       cu->cu_total = *(struct timeval *) info;
470       break;
471     case CLGET_TIMEOUT:
472       *(struct timeval *) info = cu->cu_total;
473       break;
474     case CLSET_RETRY_TIMEOUT:
475       cu->cu_wait = *(struct timeval *) info;
476       break;
477     case CLGET_RETRY_TIMEOUT:
478       *(struct timeval *) info = cu->cu_wait;
479       break;
480     case CLGET_SERVER_ADDR:
481       *(struct sockaddr_in *) info = cu->cu_raddr;
482       break;
483     case CLGET_FD:
484       *(int *)info = cu->cu_sock;
485       break;
486     case CLGET_XID:
487       /*
488        * use the knowledge that xid is the
489        * first element in the call structure *.
490        * This will get the xid of the PREVIOUS call
491        */
492       *(u_long *)info = ntohl(*(u_long *)cu->cu_outbuf);
493       break;
494     case CLSET_XID:
495       /* This will set the xid of the NEXT call */
496       *(u_long *)cu->cu_outbuf =  htonl(*(u_long *)info - 1);
497       /* decrement by 1 as clntudp_call() increments once */
498     case CLGET_VERS:
499       /*
500        * This RELIES on the information that, in the call body,
501        * the version number field is the fifth field from the
502        * begining of the RPC header. MUST be changed if the
503        * call_struct is changed
504        */
505       *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
506                                           4 * BYTES_PER_XDR_UNIT));
507       break;
508     case CLSET_VERS:
509       *(u_long *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
510         = htonl(*(u_long *)info);
511       break;
512     case CLGET_PROG:
513       /*
514        * This RELIES on the information that, in the call body,
515        * the program number field is the  field from the
516        * begining of the RPC header. MUST be changed if the
517        * call_struct is changed
518        */
519       *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
520                                           3 * BYTES_PER_XDR_UNIT));
521       break;
522     case CLSET_PROG:
523       *(u_long *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
524         = htonl(*(u_long *)info);
525       break;
526     /* The following are only possible with TI-RPC */
527     case CLGET_SVC_ADDR:
528     case CLSET_SVC_ADDR:
529     case CLSET_PUSH_TIMOD:
530     case CLSET_POP_TIMOD:
531     default:
532       return FALSE;
533     }
534   return TRUE;
535 }
536
537 static void
538 clntudp_destroy (CLIENT *cl)
539 {
540   struct cu_data *cu = (struct cu_data *) cl->cl_private;
541
542   if (cu->cu_closeit)
543     {
544       (void) __close (cu->cu_sock);
545     }
546   XDR_DESTROY (&(cu->cu_outxdrs));
547   mem_free ((caddr_t) cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
548   mem_free ((caddr_t) cl, sizeof (CLIENT));
549 }