update from main archive
[kopensolaris-gnu/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 <rpc/rpc.h>
42 #include <sys/socket.h>
43 #include <sys/ioctl.h>
44 #include <netdb.h>
45 #include <errno.h>
46 #include <rpc/pmap_clnt.h>
47
48 #ifndef errno
49 extern int errno;
50 #endif
51
52 /*
53  * UDP bases client side rpc operations
54  */
55 static enum clnt_stat   clntudp_call();
56 static void             clntudp_abort();
57 static void             clntudp_geterr();
58 static bool_t           clntudp_freeres();
59 static bool_t           clntudp_control();
60 static void             clntudp_destroy();
61
62 static struct clnt_ops udp_ops = {
63         clntudp_call,
64         clntudp_abort,
65         clntudp_geterr,
66         clntudp_freeres,
67         clntudp_destroy,
68         clntudp_control
69 };
70
71 /*
72  * Private data kept per client handle
73  */
74 struct cu_data {
75         int                cu_sock;
76         bool_t             cu_closeit;
77         struct sockaddr_in cu_raddr;
78         int                cu_rlen;
79         struct timeval     cu_wait;
80         struct timeval     cu_total;
81         struct rpc_err     cu_error;
82         XDR                cu_outxdrs;
83         u_int              cu_xdrpos;
84         u_int              cu_sendsz;
85         char               *cu_outbuf;
86         u_int              cu_recvsz;
87         char               cu_inbuf[1];
88 };
89
90 /*
91  * Create a UDP based client handle.
92  * If *sockp<0, *sockp is set to a newly created UPD socket.
93  * If raddr->sin_port is 0 a binder on the remote machine
94  * is consulted for the correct port number.
95  * NB: It is the clients responsibility to close *sockp.
96  * NB: The rpch->cl_auth is initialized to null authentication.
97  *     Caller may wish to set this something more useful.
98  *
99  * wait is the amount of time used between retransmitting a call if
100  * no response has been heard;  retransmition occurs until the actual
101  * rpc call times out.
102  *
103  * sendsz and recvsz are the maximum allowable packet sizes that can be
104  * sent and received.
105  */
106 CLIENT *
107 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
108         struct sockaddr_in *raddr;
109         u_long program;
110         u_long version;
111         struct timeval wait;
112         register int *sockp;
113         u_int sendsz;
114         u_int recvsz;
115 {
116         CLIENT *cl;
117         register struct cu_data *cu;
118         struct timeval now;
119         struct rpc_msg call_msg;
120
121         cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
122         if (cl == NULL) {
123                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
124                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
125                 rpc_createerr.cf_error.re_errno = errno;
126                 goto fooy;
127         }
128         sendsz = ((sendsz + 3) / 4) * 4;
129         recvsz = ((recvsz + 3) / 4) * 4;
130         cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
131         if (cu == NULL) {
132                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
133                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
134                 rpc_createerr.cf_error.re_errno = errno;
135                 goto fooy;
136         }
137         cu->cu_outbuf = &cu->cu_inbuf[recvsz];
138
139         (void)gettimeofday(&now, (struct timezone *)0);
140         if (raddr->sin_port == 0) {
141                 u_short port;
142                 if ((port =
143                     pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
144                         goto fooy;
145                 }
146                 raddr->sin_port = htons(port);
147         }
148         cl->cl_ops = &udp_ops;
149         cl->cl_private = (caddr_t)cu;
150         cu->cu_raddr = *raddr;
151         cu->cu_rlen = sizeof (cu->cu_raddr);
152         cu->cu_wait = wait;
153         cu->cu_total.tv_sec = -1;
154         cu->cu_total.tv_usec = -1;
155         cu->cu_sendsz = sendsz;
156         cu->cu_recvsz = recvsz;
157         call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
158         call_msg.rm_direction = CALL;
159         call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
160         call_msg.rm_call.cb_prog = program;
161         call_msg.rm_call.cb_vers = version;
162         xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
163             sendsz, XDR_ENCODE);
164         if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
165                 goto fooy;
166         }
167         cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
168         if (*sockp < 0) {
169                 int dontblock = 1;
170
171                 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
172                 if (*sockp < 0) {
173                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
174                         rpc_createerr.cf_error.re_errno = errno;
175                         goto fooy;
176                 }
177                 /* attempt to bind to prov port */
178                 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
179                 /* the sockets rpc controls are non-blocking */
180                 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
181                 cu->cu_closeit = TRUE;
182         } else {
183                 cu->cu_closeit = FALSE;
184         }
185         cu->cu_sock = *sockp;
186         cl->cl_auth = authnone_create();
187         return (cl);
188 fooy:
189         if (cu)
190                 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
191         if (cl)
192                 mem_free((caddr_t)cl, sizeof(CLIENT));
193         return ((CLIENT *)NULL);
194 }
195
196 CLIENT *
197 clntudp_create(raddr, program, version, wait, sockp)
198         struct sockaddr_in *raddr;
199         u_long program;
200         u_long version;
201         struct timeval wait;
202         register int *sockp;
203 {
204
205         return(clntudp_bufcreate(raddr, program, version, wait, sockp,
206             UDPMSGSIZE, UDPMSGSIZE));
207 }
208
209 static enum clnt_stat
210 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
211         register CLIENT *cl;            /* client handle */
212         u_long          proc;           /* procedure number */
213         xdrproc_t       xargs;          /* xdr routine for args */
214         caddr_t         argsp;          /* pointer to args */
215         xdrproc_t       xresults;       /* xdr routine for results */
216         caddr_t         resultsp;       /* pointer to results */
217         struct timeval  utimeout;       /* seconds to wait before giving up */
218 {
219         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
220         register XDR *xdrs;
221         register int outlen;
222         register int inlen;
223         int fromlen;
224 #ifdef FD_SETSIZE
225         fd_set readfds;
226         fd_set mask;
227 #else
228         int readfds;
229         register int mask;
230 #endif /* def FD_SETSIZE */
231         struct sockaddr_in from;
232         struct rpc_msg reply_msg;
233         XDR reply_xdrs;
234         struct timeval time_waited;
235         bool_t ok;
236         int nrefreshes = 2;     /* number of times to refresh cred */
237         struct timeval timeout;
238
239         if (cu->cu_total.tv_usec == -1) {
240                 timeout = utimeout;     /* use supplied timeout */
241         } else {
242                 timeout = cu->cu_total; /* use default timeout */
243         }
244
245         time_waited.tv_sec = 0;
246         time_waited.tv_usec = 0;
247 call_again:
248         xdrs = &(cu->cu_outxdrs);
249         xdrs->x_op = XDR_ENCODE;
250         XDR_SETPOS(xdrs, cu->cu_xdrpos);
251         /*
252          * the transaction is the first thing in the out buffer
253          */
254         (*(u_short *)(cu->cu_outbuf))++;
255         if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
256             (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
257             (! (*xargs)(xdrs, argsp)))
258                 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
259         outlen = (int)XDR_GETPOS(xdrs);
260
261 send_again:
262         if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
263             (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
264             != outlen) {
265                 cu->cu_error.re_errno = errno;
266                 return (cu->cu_error.re_status = RPC_CANTSEND);
267         }
268
269         /*
270          * Hack to provide rpc-based message passing
271          */
272         if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
273                 return (cu->cu_error.re_status = RPC_TIMEDOUT);
274         }
275         /*
276          * sub-optimal code appears here because we have
277          * some clock time to spare while the packets are in flight.
278          * (We assume that this is actually only executed once.)
279          */
280         reply_msg.acpted_rply.ar_verf = _null_auth;
281         reply_msg.acpted_rply.ar_results.where = resultsp;
282         reply_msg.acpted_rply.ar_results.proc = xresults;
283 #ifdef FD_SETSIZE
284         FD_ZERO(&mask);
285         FD_SET(cu->cu_sock, &mask);
286 #else
287         mask = 1 << cu->cu_sock;
288 #endif /* def FD_SETSIZE */
289         for (;;) {
290                 readfds = mask;
291                 switch (select(_rpc_dtablesize(), &readfds, (int *)NULL,
292                                (int *)NULL, &(cu->cu_wait))) {
293
294                 case 0:
295                         time_waited.tv_sec += cu->cu_wait.tv_sec;
296                         time_waited.tv_usec += cu->cu_wait.tv_usec;
297                         while (time_waited.tv_usec >= 1000000) {
298                                 time_waited.tv_sec++;
299                                 time_waited.tv_usec -= 1000000;
300                         }
301                         if ((time_waited.tv_sec < timeout.tv_sec) ||
302                                 ((time_waited.tv_sec == timeout.tv_sec) &&
303                                 (time_waited.tv_usec < timeout.tv_usec)))
304                                 goto send_again;
305                         return (cu->cu_error.re_status = RPC_TIMEDOUT);
306
307                 /*
308                  * buggy in other cases because time_waited is not being
309                  * updated.
310                  */
311                 case -1:
312                         if (errno == EINTR)
313                                 continue;
314                         cu->cu_error.re_errno = errno;
315                         return (cu->cu_error.re_status = RPC_CANTRECV);
316                 }
317                 do {
318                         fromlen = sizeof(struct sockaddr);
319                         inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
320                                 (int) cu->cu_recvsz, 0,
321                                 (struct sockaddr *)&from, &fromlen);
322                 } while (inlen < 0 && errno == EINTR);
323                 if (inlen < 0) {
324                         if (errno == EWOULDBLOCK)
325                                 continue;
326                         cu->cu_error.re_errno = errno;
327                         return (cu->cu_error.re_status = RPC_CANTRECV);
328                 }
329                 if (inlen < 4)
330                         continue;
331                 /* see if reply transaction id matches sent id */
332                 if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
333                         continue;
334                 /* we now assume we have the proper reply */
335                 break;
336         }
337
338         /*
339          * now decode and validate the response
340          */
341         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
342         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
343         /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
344         if (ok) {
345                 _seterr_reply(&reply_msg, &(cu->cu_error));
346                 if (cu->cu_error.re_status == RPC_SUCCESS) {
347                         if (! AUTH_VALIDATE(cl->cl_auth,
348                                 &reply_msg.acpted_rply.ar_verf)) {
349                                 cu->cu_error.re_status = RPC_AUTHERROR;
350                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
351                         }
352                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
353                                 xdrs->x_op = XDR_FREE;
354                                 (void)xdr_opaque_auth(xdrs,
355                                     &(reply_msg.acpted_rply.ar_verf));
356                         }
357                 }  /* end successful completion */
358                 else {
359                         /* maybe our credentials need to be refreshed ... */
360                         if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
361                                 nrefreshes--;
362                                 goto call_again;
363                         }
364                 }  /* end of unsuccessful completion */
365         }  /* end of valid reply message */
366         else {
367                 cu->cu_error.re_status = RPC_CANTDECODERES;
368         }
369         return (cu->cu_error.re_status);
370 }
371
372 static void
373 clntudp_geterr(cl, errp)
374         CLIENT *cl;
375         struct rpc_err *errp;
376 {
377         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
378
379         *errp = cu->cu_error;
380 }
381
382
383 static bool_t
384 clntudp_freeres(cl, xdr_res, res_ptr)
385         CLIENT *cl;
386         xdrproc_t xdr_res;
387         caddr_t res_ptr;
388 {
389         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
390         register XDR *xdrs = &(cu->cu_outxdrs);
391
392         xdrs->x_op = XDR_FREE;
393         return ((*xdr_res)(xdrs, res_ptr));
394 }
395
396 static void
397 clntudp_abort(/*h*/)
398         /*CLIENT *h;*/
399 {
400 }
401
402 static bool_t
403 clntudp_control(cl, request, info)
404         CLIENT *cl;
405         int request;
406         char *info;
407 {
408         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
409
410         switch (request) {
411         case CLSET_TIMEOUT:
412                 cu->cu_total = *(struct timeval *)info;
413                 break;
414         case CLGET_TIMEOUT:
415                 *(struct timeval *)info = cu->cu_total;
416                 break;
417         case CLSET_RETRY_TIMEOUT:
418                 cu->cu_wait = *(struct timeval *)info;
419                 break;
420         case CLGET_RETRY_TIMEOUT:
421                 *(struct timeval *)info = cu->cu_wait;
422                 break;
423         case CLGET_SERVER_ADDR:
424                 *(struct sockaddr_in *)info = cu->cu_raddr;
425                 break;
426         default:
427                 return (FALSE);
428         }
429         return (TRUE);
430 }
431
432 static void
433 clntudp_destroy(cl)
434         CLIENT *cl;
435 {
436         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
437
438         if (cu->cu_closeit) {
439                 (void)close(cu->cu_sock);
440         }
441         XDR_DESTROY(&(cu->cu_outxdrs));
442         mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
443         mem_free((caddr_t)cl, sizeof(CLIENT));
444 }