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