Merge up from branch.
[shibboleth/cpp-sp.git] / oncrpc / clnt_udp.c
1 /*********************************************************************
2  * RPC for the Windows NT Operating System
3  * 1993 by Martin F. Gergeleit
4  * Users may use, copy or modify Sun RPC for the Windows NT Operating 
5  * System according to the Sun copyright below.
6  *
7  * RPC for the Windows NT Operating System COMES WITH ABSOLUTELY NO 
8  * WARRANTY, NOR WILL I BE LIABLE FOR ANY DAMAGES INCURRED FROM THE 
9  * USE OF. USE ENTIRELY AT YOUR OWN RISK!!!
10  *********************************************************************/
11
12 /* @(#)clnt_udp.c       2.2 88/08/01 4.0 RPCSRC */
13 /*
14  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
15  * unrestricted use provided that this legend is included on all tape
16  * media and as a part of the software program in whole or part.  Users
17  * may copy or modify Sun RPC without charge, but are not authorized
18  * to license or distribute it to anyone else except as part of a product or
19  * program developed by the user.
20  *
21  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
22  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
24  *
25  * Sun RPC is provided with no support and without any obligation on the
26  * part of Sun Microsystems, Inc. to assist in its use, correction,
27  * modification or enhancement.
28  *
29  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
30  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
31  * OR ANY PART THEREOF.
32  *
33  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
34  * or profits or other special, indirect and consequential damages, even if
35  * Sun has been advised of the possibility of such damages.
36  *
37  * Sun Microsystems, Inc.
38  * 2550 Garcia Avenue
39  * Mountain View, California  94043
40  */
41 #if !defined(lint) && defined(SCCSIDS)
42 static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
43 #endif
44
45 /*
46  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
47  *
48  * Copyright (C) 1984, Sun Microsystems, Inc.
49  */
50
51 #include <stdio.h>
52 #include <rpc/rpc.h>
53 #ifdef WIN32
54 #include <errno.h>
55 #include <rpc/pmap_clnt.h>
56 #else
57 #include <sys/socket.h>
58 #include <sys/ioctl.h>
59 #include <netdb.h>
60 #include <errno.h>
61 #include <rpc/pmap_clnt.h>
62
63 #ifndef FIONBIO
64 #include <sys/filio.h>
65 #endif
66
67 extern int errno;
68 #endif
69
70 /*
71  * UDP bases client side rpc operations
72  */
73 static enum clnt_stat   clntudp_call();
74 static void             clntudp_abort();
75 static void             clntudp_geterr();
76 static bool_t           clntudp_freeres();
77 static bool_t           clntudp_control();
78 static void             clntudp_destroy();
79
80 static struct clnt_ops udp_ops = {
81         clntudp_call,
82         clntudp_abort,
83         clntudp_geterr,
84         clntudp_freeres,
85         clntudp_destroy,
86         clntudp_control
87 };
88
89 /*
90  * Private data kept per client handle
91  */
92 struct cu_data {
93         int                cu_sock;
94         bool_t             cu_closeit;
95         struct sockaddr_in cu_raddr;
96         int                cu_rlen;
97         struct timeval     cu_wait;
98         struct timeval     cu_total;
99         struct rpc_err     cu_error;
100         XDR                cu_outxdrs;
101         u_int              cu_xdrpos;
102         u_int              cu_sendsz;
103         char               *cu_outbuf;
104         u_int              cu_recvsz;
105         char               cu_inbuf[1];
106 };
107
108 /*
109  * Create a UDP based client handle.
110  * If *sockp<0, *sockp is set to a newly created UPD socket.
111  * If raddr->sin_port is 0 a binder on the remote machine
112  * is consulted for the correct port number.
113  * NB: It is the clients responsibility to close *sockp.
114  * NB: The rpch->cl_auth is initialized to null authentication.
115  *     Caller may wish to set this something more useful.
116  *
117  * wait is the amount of time used between retransmitting a call if
118  * no response has been heard;  retransmition occurs until the actual
119  * rpc call times out.
120  *
121  * sendsz and recvsz are the maximum allowable packet sizes that can be
122  * sent and received.
123  */
124 CLIENT *
125 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
126         struct sockaddr_in *raddr;
127         u_long program;
128         u_long version;
129         struct timeval wait;
130         register int *sockp;
131         u_int sendsz;
132         u_int recvsz;
133 {
134         CLIENT *cl;
135         register struct cu_data *cu;
136         struct timeval now;
137         struct rpc_msg call_msg;
138
139         cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
140         if (cl == NULL) {
141 #ifdef WIN32
142                 nt_rpc_report("clntudp_create: out of memory\n");
143                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
144                 rpc_createerr.cf_error.re_errno = ENOMEM;
145 #else
146                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
147                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
148                 rpc_createerr.cf_error.re_errno = errno;
149 #endif
150                 goto fooy;
151         }
152         sendsz = ((sendsz + 3) / 4) * 4;
153         recvsz = ((recvsz + 3) / 4) * 4;
154         cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
155         if (cu == NULL) {
156 #ifdef WIN32
157                 nt_rpc_report("clntudp_create: out of memory\n");
158                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
159                 rpc_createerr.cf_error.re_errno = ENOMEM;
160 #else
161                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
162                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
163                 rpc_createerr.cf_error.re_errno = errno;
164 #endif
165                 goto fooy;
166         }
167         cu->cu_outbuf = &cu->cu_inbuf[recvsz];
168
169         (void)gettimeofday(&now, (struct timezone *)0);
170         if (raddr->sin_port == 0) {
171                 u_short port;
172                 if ((port =
173                     pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
174                         goto fooy;
175                 }
176                 raddr->sin_port = htons(port);
177         }
178         cl->cl_ops = &udp_ops;
179         cl->cl_private = (caddr_t)cu;
180         cu->cu_raddr = *raddr;
181         cu->cu_rlen = sizeof (cu->cu_raddr);
182         cu->cu_wait = wait;
183         cu->cu_total.tv_sec = -1;
184         cu->cu_total.tv_usec = -1;
185         cu->cu_sendsz = sendsz;
186         cu->cu_recvsz = recvsz;
187         call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
188         call_msg.rm_direction = CALL;
189         call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
190         call_msg.rm_call.cb_prog = program;
191         call_msg.rm_call.cb_vers = version;
192         xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
193             sendsz, XDR_ENCODE);
194         if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
195                 goto fooy;
196         }
197         cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
198         if (*sockp < 0) {
199                 int dontblock = 1;
200
201                 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
202 #ifdef WIN32
203                 if (*sockp == INVALID_SOCKET) {
204                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
205                         rpc_createerr.cf_error.re_errno = WSAerrno;
206 #else
207                 if (*sockp < 0) {
208                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
209                         rpc_createerr.cf_error.re_errno = errno;
210 #endif
211                         goto fooy;
212                 }
213                 /* attempt to bind to prov port */
214                 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
215                 /* the sockets rpc controls are non-blocking */
216 #ifdef WIN32
217                 (void)ioctlsocket(*sockp, FIONBIO, (char *) &dontblock);
218 #else
219                 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
220 #endif
221                 cu->cu_closeit = TRUE;
222         } else {
223                 cu->cu_closeit = FALSE;
224         }
225         cu->cu_sock = *sockp;
226         cl->cl_auth = authnone_create();
227         return (cl);
228 fooy:
229         if (cu)
230                 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
231         if (cl)
232                 mem_free((caddr_t)cl, sizeof(CLIENT));
233         return ((CLIENT *)NULL);
234 }
235
236 CLIENT *
237 clntudp_create(raddr, program, version, wait, sockp)
238         struct sockaddr_in *raddr;
239         u_long program;
240         u_long version;
241         struct timeval wait;
242         register int *sockp;
243 {
244
245         return(clntudp_bufcreate(raddr, program, version, wait, sockp,
246             UDPMSGSIZE, UDPMSGSIZE));
247 }
248
249 static enum clnt_stat
250 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
251         register CLIENT *cl;            /* client handle */
252         u_long          proc;           /* procedure number */
253         xdrproc_t       xargs;          /* xdr routine for args */
254         caddr_t         argsp;          /* pointer to args */
255         xdrproc_t       xresults;       /* xdr routine for results */
256         caddr_t         resultsp;       /* pointer to results */
257         struct timeval  utimeout;       /* seconds to wait before giving up */
258 {
259         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
260         register XDR *xdrs;
261         register int outlen;
262         register int inlen;
263         int fromlen;
264 #ifdef FD_SETSIZE
265         fd_set readfds;
266         fd_set mask;
267 #else
268         int readfds;
269         register int mask;
270 #endif /* def FD_SETSIZE */
271         struct sockaddr_in from;
272         struct rpc_msg reply_msg;
273         XDR reply_xdrs;
274         struct timeval time_waited;
275         bool_t ok;
276         int nrefreshes = 2;     /* number of times to refresh cred */
277         struct timeval timeout;
278
279         if (cu->cu_total.tv_usec == -1) {
280                 timeout = utimeout;     /* use supplied timeout */
281         } else {
282                 timeout = cu->cu_total; /* use default timeout */
283         }
284
285         time_waited.tv_sec = 0;
286         time_waited.tv_usec = 0;
287 call_again:
288         xdrs = &(cu->cu_outxdrs);
289         xdrs->x_op = XDR_ENCODE;
290         XDR_SETPOS(xdrs, cu->cu_xdrpos);
291         /*
292          * the transaction is the first thing in the out buffer
293          */
294         (*(u_short *)(cu->cu_outbuf))++;
295
296         if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
297             (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
298             (! (*xargs)(xdrs, argsp)))
299                 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
300
301         outlen = (int)XDR_GETPOS(xdrs);
302
303 send_again:
304         if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
305             (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
306             != outlen) {
307 #ifdef WIN32
308                 cu->cu_error.re_errno = WSAerrno;
309 #else
310                 cu->cu_error.re_errno = errno;
311 #endif
312                 return (cu->cu_error.re_status = RPC_CANTSEND);
313         }
314
315         /*
316          * Hack to provide rpc-based message passing
317          */
318         if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
319                 return (cu->cu_error.re_status = RPC_TIMEDOUT);
320         }
321         /*
322          * sub-optimal code appears here because we have
323          * some clock time to spare while the packets are in flight.
324          * (We assume that this is actually only executed once.)
325          */
326         reply_msg.acpted_rply.ar_verf = _null_auth;
327         reply_msg.acpted_rply.ar_results.where = resultsp;
328         reply_msg.acpted_rply.ar_results.proc = xresults;
329 #ifdef FD_SETSIZE
330         FD_ZERO(&mask);
331         FD_SET(cu->cu_sock, &mask);
332 #else
333         mask = 1 << cu->cu_sock;
334 #endif /* def FD_SETSIZE */
335         for (;;) {
336                 readfds = mask;
337 #ifdef WIN32
338                 switch (select(0 /* unused in winsock */, &readfds, (int *)NULL, (int *)NULL,
339 #else
340                  switch (select(FD_SETSIZE, &readfds, NULL, NULL,
341 #endif
342                                &(cu->cu_wait))) {
343
344                 case 0:
345                         time_waited.tv_sec += cu->cu_wait.tv_sec;
346                         time_waited.tv_usec += cu->cu_wait.tv_usec;
347                         while (time_waited.tv_usec >= 1000000) {
348                                 time_waited.tv_sec++;
349                                 time_waited.tv_usec -= 1000000;
350                         }
351                         if ((time_waited.tv_sec < timeout.tv_sec) ||
352                                 ((time_waited.tv_sec == timeout.tv_sec) &&
353                                 (time_waited.tv_usec < timeout.tv_usec)))
354                                 goto send_again;
355                         return (cu->cu_error.re_status = RPC_TIMEDOUT);
356
357                 /*
358                  * buggy in other cases because time_waited is not being
359                  * updated.
360                  */
361                 case -1:
362 #ifdef WIN32
363                         if (WSAerrno == WSAEINTR)
364                                 continue;
365                         cu->cu_error.re_errno = WSAerrno;
366 #else
367                         if (errno == EINTR)
368                                 continue;       
369                         cu->cu_error.re_errno = errno;
370 #endif
371                         return (cu->cu_error.re_status = RPC_CANTRECV);
372                 }
373                 do {
374                         fromlen = sizeof(struct sockaddr);
375                         inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
376                                 (int) cu->cu_recvsz, 0,
377                                 (struct sockaddr *)&from, &fromlen);
378 #ifdef WIN32
379                 } while (inlen < 0 && WSAerrno == EINTR);
380                 if (inlen < 0) {
381                         if (WSAerrno == WSAEWOULDBLOCK)
382                                 continue;
383                         cu->cu_error.re_errno = WSAerrno;
384 #else
385                 } while (inlen < 0 && errno == EINTR);
386                 if (inlen < 0) {
387                         if (errno == EWOULDBLOCK)
388                                 continue;       
389                         cu->cu_error.re_errno = errno;
390 #endif
391                         return (cu->cu_error.re_status = RPC_CANTRECV);
392                 }
393                 if (inlen < sizeof(u_long))
394                         continue;
395                 /* see if reply transaction id matches sent id */
396                 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
397                         continue;
398                 /* we now assume we have the proper reply */
399                 break;
400         }
401
402         /*
403          * now decode and validate the response
404          */
405         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
406         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
407         /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
408         if (ok) {
409                 _seterr_reply(&reply_msg, &(cu->cu_error));
410                 if (cu->cu_error.re_status == RPC_SUCCESS) {
411                         if (! AUTH_VALIDATE(cl->cl_auth,
412                                 &reply_msg.acpted_rply.ar_verf)) {
413                                 cu->cu_error.re_status = RPC_AUTHERROR;
414                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
415                         }
416                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
417                                 xdrs->x_op = XDR_FREE;
418                                 (void)xdr_opaque_auth(xdrs,
419                                     &(reply_msg.acpted_rply.ar_verf));
420                         }
421                 }  /* end successful completion */
422                 else {
423                         /* maybe our credentials need to be refreshed ... */
424                         if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
425                                 nrefreshes--;
426                                 goto call_again;
427                         }
428                 }  /* end of unsuccessful completion */
429         }  /* end of valid reply message */
430         else {
431                 cu->cu_error.re_status = RPC_CANTDECODERES;
432         }
433         return (cu->cu_error.re_status);
434 }
435
436 static void
437 clntudp_geterr(cl, errp)
438         CLIENT *cl;
439         struct rpc_err *errp;
440 {
441         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
442
443         *errp = cu->cu_error;
444 }
445
446
447 static bool_t
448 clntudp_freeres(cl, xdr_res, res_ptr)
449         CLIENT *cl;
450         xdrproc_t xdr_res;
451         caddr_t res_ptr;
452 {
453         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
454         register XDR *xdrs = &(cu->cu_outxdrs);
455
456         xdrs->x_op = XDR_FREE;
457         return ((*xdr_res)(xdrs, res_ptr));
458 }
459
460 static void
461 clntudp_abort(/*h*/)
462         /*CLIENT *h;*/
463 {
464 }
465
466 static bool_t
467 clntudp_control(cl, request, info)
468         CLIENT *cl;
469         int request;
470         char *info;
471 {
472         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
473
474         switch (request) {
475         case CLSET_TIMEOUT:
476                 cu->cu_total = *(struct timeval *)info;
477                 break;
478         case CLGET_TIMEOUT:
479                 *(struct timeval *)info = cu->cu_total;
480                 break;
481         case CLSET_RETRY_TIMEOUT:
482                 cu->cu_wait = *(struct timeval *)info;
483                 break;
484         case CLGET_RETRY_TIMEOUT:
485                 *(struct timeval *)info = cu->cu_wait;
486                 break;
487         case CLGET_SERVER_ADDR:
488                 *(struct sockaddr_in *)info = cu->cu_raddr;
489                 break;
490         default:
491                 return (FALSE);
492         }
493         return (TRUE);
494 }
495
496 static void
497 clntudp_destroy(cl)
498         CLIENT *cl;
499 {
500         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
501
502         if (cu->cu_closeit) {
503 #ifdef WIN32
504                 (void)closesocket(cu->cu_sock);
505 #else
506                 (void)close(cu->cu_sock);
507 #endif
508         }
509         XDR_DESTROY(&(cu->cu_outxdrs));
510         mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
511         mem_free((caddr_t)cl, sizeof(CLIENT));
512 }