f38a8586a4fafb18bc9a33ee3457bf7175792140
[kopensolaris-gnu/glibc.git] / sunrpc / clnt_udp6.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 clntudp6_call (CLIENT *, u_long, xdrproc_t, caddr_t,
60                                      xdrproc_t, caddr_t, struct timeval);
61 static void clntudp6_abort (void);
62 static void clntudp6_geterr (CLIENT *, struct rpc_err *);
63 static bool_t clntudp6_freeres (CLIENT *, xdrproc_t, caddr_t);
64 static bool_t clntudp6_control (CLIENT *, int, char *);
65 static void clntudp6_destroy (CLIENT *);
66
67 static struct clnt_ops udp6_ops =
68 {
69   clntudp6_call,
70   clntudp6_abort,
71   clntudp6_geterr,
72   clntudp6_freeres,
73   clntudp6_destroy,
74   clntudp6_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 clntudp6_bufcreate (struct sockaddr_in6 *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->sin6_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->sin6_port = htons (port);
151     }
152   cl->cl_ops = &udp6_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_INET6, 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) bindresvport6 (*sockp, (struct sockaddr_in6 *) 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 clntudp6_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 clntudp6_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 clntudp6_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   anyup = 0;
327   for (;;)
328     {
329       switch (__poll (&fd, 1, milliseconds))
330         {
331
332         case 0:
333           if (anyup == 0)
334             {
335               anyup = is_network_up (cu->cu_sock);
336               if (!anyup)
337                 return (cu->cu_error.re_status = RPC_CANTRECV);
338             }
339
340           time_waited.tv_sec += cu->cu_wait.tv_sec;
341           time_waited.tv_usec += cu->cu_wait.tv_usec;
342           while (time_waited.tv_usec >= 1000000)
343             {
344               time_waited.tv_sec++;
345               time_waited.tv_usec -= 1000000;
346             }
347           if ((time_waited.tv_sec < timeout.tv_sec) ||
348               ((time_waited.tv_sec == timeout.tv_sec) &&
349                (time_waited.tv_usec < timeout.tv_usec)))
350             goto send_again;
351           return (cu->cu_error.re_status = RPC_TIMEDOUT);
352
353           /*
354            * buggy in other cases because time_waited is not being
355            * updated.
356            */
357         case -1:
358           if (errno == EINTR)
359             continue;
360           cu->cu_error.re_errno = errno;
361           return (cu->cu_error.re_status = RPC_CANTRECV);
362         }
363       do
364         {
365           fromlen = sizeof (struct sockaddr);
366           inlen = recvfrom (cu->cu_sock, cu->cu_inbuf,
367                             (int) cu->cu_recvsz, 0,
368                             (struct sockaddr *) &from, &fromlen);
369         }
370       while (inlen < 0 && errno == EINTR);
371       if (inlen < 0)
372         {
373           if (errno == EWOULDBLOCK)
374             continue;
375           cu->cu_error.re_errno = errno;
376           return (cu->cu_error.re_status = RPC_CANTRECV);
377         }
378       if (inlen < 4)
379         continue;
380
381       /* see if reply transaction id matches sent id.
382         Don't do this if we only wait for a replay */
383       if (xargs != NULL
384           && (*((u_int32_t *) (cu->cu_inbuf))
385               != *((u_int32_t *) (cu->cu_outbuf))))
386         continue;
387       /* we now assume we have the proper reply */
388       break;
389     }
390
391   /*
392    * now decode and validate the response
393    */
394   xdrmem_create (&reply_xdrs, cu->cu_inbuf, (u_int) inlen, XDR_DECODE);
395   ok = xdr_replymsg (&reply_xdrs, &reply_msg);
396   /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
397   if (ok)
398     {
399       _seterr_reply (&reply_msg, &(cu->cu_error));
400       if (cu->cu_error.re_status == RPC_SUCCESS)
401         {
402           if (!AUTH_VALIDATE (cl->cl_auth,
403                               &reply_msg.acpted_rply.ar_verf))
404             {
405               cu->cu_error.re_status = RPC_AUTHERROR;
406               cu->cu_error.re_why = AUTH_INVALIDRESP;
407             }
408           if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
409             {
410               xdrs->x_op = XDR_FREE;
411               (void) xdr_opaque_auth (xdrs,
412                                       &(reply_msg.acpted_rply.ar_verf));
413             }
414         }                       /* end successful completion */
415       else
416         {
417           /* maybe our credentials need to be refreshed ... */
418           if (nrefreshes > 0 && AUTH_REFRESH (cl->cl_auth))
419             {
420               nrefreshes--;
421               goto call_again;
422             }
423         }                       /* end of unsuccessful completion */
424     }                           /* end of valid reply message */
425   else
426     {
427       cu->cu_error.re_status = RPC_CANTDECODERES;
428     }
429   return cu->cu_error.re_status;
430 }
431
432 static void
433 clntudp6_geterr (CLIENT *cl, struct rpc_err *errp)
434 {
435   struct cu_data *cu = (struct cu_data *) cl->cl_private;
436
437   *errp = cu->cu_error;
438 }
439
440
441 static bool_t
442 clntudp6_freeres (CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
443 {
444   struct cu_data *cu = (struct cu_data *) cl->cl_private;
445   XDR *xdrs = &(cu->cu_outxdrs);
446
447   xdrs->x_op = XDR_FREE;
448   return (*xdr_res) (xdrs, res_ptr);
449 }
450
451 static void
452 clntudp6_abort (void)
453 {
454 }
455
456 static bool_t
457 clntudp6_control (CLIENT *cl, int request, char *info)
458 {
459   struct cu_data *cu = (struct cu_data *) cl->cl_private;
460
461   switch (request)
462     {
463     case CLSET_FD_CLOSE:
464       cu->cu_closeit = TRUE;
465       break;
466     case CLSET_FD_NCLOSE:
467       cu->cu_closeit = FALSE;
468       break;
469     case CLSET_TIMEOUT:
470       cu->cu_total = *(struct timeval *) info;
471       break;
472     case CLGET_TIMEOUT:
473       *(struct timeval *) info = cu->cu_total;
474       break;
475     case CLSET_RETRY_TIMEOUT:
476       cu->cu_wait = *(struct timeval *) info;
477       break;
478     case CLGET_RETRY_TIMEOUT:
479       *(struct timeval *) info = cu->cu_wait;
480       break;
481     case CLGET_SERVER_ADDR:
482       *(struct sockaddr_in *) info = cu->cu_raddr;
483       break;
484     case CLGET_FD:
485       *(int *)info = cu->cu_sock;
486       break;
487     case CLGET_XID:
488       /*
489        * use the knowledge that xid is the
490        * first element in the call structure *.
491        * This will get the xid of the PREVIOUS call
492        */
493       *(u_long *)info = ntohl(*(u_long *)cu->cu_outbuf);
494       break;
495     case CLSET_XID:
496       /* This will set the xid of the NEXT call */
497       *(u_long *)cu->cu_outbuf =  htonl(*(u_long *)info - 1);
498       /* decrement by 1 as clntudp_call() increments once */
499     case CLGET_VERS:
500       /*
501        * This RELIES on the information that, in the call body,
502        * the version number field is the fifth field from the
503        * begining of the RPC header. MUST be changed if the
504        * call_struct is changed
505        */
506       *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
507                                           4 * BYTES_PER_XDR_UNIT));
508       break;
509     case CLSET_VERS:
510       *(u_long *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
511         = htonl(*(u_long *)info);
512       break;
513     case CLGET_PROG:
514       /*
515        * This RELIES on the information that, in the call body,
516        * the program number field is the  field from the
517        * begining of the RPC header. MUST be changed if the
518        * call_struct is changed
519        */
520       *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
521                                           3 * BYTES_PER_XDR_UNIT));
522       break;
523     case CLSET_PROG:
524       *(u_long *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
525         = htonl(*(u_long *)info);
526       break;
527     /* The following are only possible with TI-RPC */
528     case CLGET_SVC_ADDR:
529     case CLSET_SVC_ADDR:
530     case CLSET_PUSH_TIMOD:
531     case CLSET_POP_TIMOD:
532     default:
533       return FALSE;
534     }
535   return TRUE;
536 }
537
538 static void
539 clntudp6_destroy (CLIENT *cl)
540 {
541   struct cu_data *cu = (struct cu_data *) cl->cl_private;
542
543   if (cu->cu_closeit)
544     {
545       (void) __close (cu->cu_sock);
546     }
547   XDR_DESTROY (&(cu->cu_outxdrs));
548   mem_free ((caddr_t) cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
549   mem_free ((caddr_t) cl, sizeof (CLIENT));
550 }