Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libcr / rpc / clnt_udp.c
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  */
29
30 #if defined(LIBC_SCCS) && !defined(lint)
31 /*static char *sccsid = "from: @(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/
32 /*static char *sccsid = "from: @(#)clnt_udp.c   2.2 88/08/01 4.0 RPCSRC";*/
33 static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_udp.c,v 1.15.2.1 2001/06/28 21:44:24 iedowse Exp $";
34 #endif
35
36 /*
37  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
38  *
39  * Copyright (C) 1984, Sun Microsystems, Inc.
40  */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <rpc/rpc.h>
47 #include <sys/socket.h>
48 #include <sys/ioctl.h>
49 #include <netdb.h>
50 #include <errno.h>
51 #include <rpc/pmap_clnt.h>
52
53 /*
54  * UDP bases client side rpc operations
55  */
56 static enum clnt_stat   clntudp_call();
57 static void             clntudp_abort();
58 static void             clntudp_geterr();
59 static bool_t           clntudp_freeres();
60 static bool_t           clntudp_control();
61 static void             clntudp_destroy();
62
63 static struct clnt_ops udp_ops = {
64         clntudp_call,
65         clntudp_abort,
66         clntudp_geterr,
67         clntudp_freeres,
68         clntudp_destroy,
69         clntudp_control
70 };
71
72 /*
73  * Private data kept per client handle
74  */
75 struct cu_data {
76         int                cu_sock;
77         bool_t             cu_closeit;
78         struct sockaddr_in cu_raddr;
79         int                cu_rlen;
80         struct timeval     cu_wait;
81         struct timeval     cu_total;
82         struct rpc_err     cu_error;
83         XDR                cu_outxdrs;
84         u_int              cu_xdrpos;
85         u_int              cu_sendsz;
86         char               *cu_outbuf;
87         u_int              cu_recvsz;
88         int                cu_connect;          /* Use connect(). */
89         int                cu_connected;        /* Have done connect(). */
90         char               cu_inbuf[1];
91 };
92
93 /*
94  * Create a UDP based client handle.
95  * If *sockp<0, *sockp is set to a newly created UPD socket.
96  * If raddr->sin_port is 0 a binder on the remote machine
97  * is consulted for the correct port number.
98  * NB: It is the clients responsibility to close *sockp.
99  * NB: The rpch->cl_auth is initialized to null authentication.
100  *     Caller may wish to set this something more useful.
101  *
102  * wait is the amount of time used between retransmitting a call if
103  * no response has been heard;  retransmition occurs until the actual
104  * rpc call times out.
105  *
106  * sendsz and recvsz are the maximum allowable packet sizes that can be
107  * sent and received.
108  */
109 CLIENT *
110 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
111         struct sockaddr_in *raddr;
112         u_long program;
113         u_long version;
114         struct timeval wait;
115         register int *sockp;
116         u_int sendsz;
117         u_int recvsz;
118 {
119         CLIENT *cl;
120         register struct cu_data *cu = NULL;
121         struct timeval now;
122         struct rpc_msg call_msg;
123         static u_int32_t disrupt;
124
125         if (disrupt == 0)
126                 disrupt = (u_int32_t)(long)raddr;
127
128         cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
129         if (cl == NULL) {
130                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
131                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
132                 rpc_createerr.cf_error.re_errno = errno;
133                 goto fooy;
134         }
135         sendsz = ((sendsz + 3) / 4) * 4;
136         recvsz = ((recvsz + 3) / 4) * 4;
137         cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
138         if (cu == NULL) {
139                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
140                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
141                 rpc_createerr.cf_error.re_errno = errno;
142                 goto fooy;
143         }
144         cu->cu_outbuf = &cu->cu_inbuf[recvsz];
145
146         (void)gettimeofday(&now, (struct timezone *)0);
147         if (raddr->sin_port == 0) {
148                 u_short port;
149                 if ((port =
150                     pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
151                         goto fooy;
152                 }
153                 raddr->sin_port = htons(port);
154         }
155         cl->cl_ops = &udp_ops;
156         cl->cl_private = (caddr_t)cu;
157         cu->cu_raddr = *raddr;
158         cu->cu_rlen = sizeof (cu->cu_raddr);
159         cu->cu_wait = wait;
160         cu->cu_total.tv_sec = -1;
161         cu->cu_total.tv_usec = -1;
162         cu->cu_sendsz = sendsz;
163         cu->cu_recvsz = recvsz;
164         cu->cu_connect = FALSE;
165         cu->cu_connected = FALSE;
166         call_msg.rm_xid = (++disrupt) ^ getpid() ^ now.tv_sec ^ now.tv_usec;
167         call_msg.rm_direction = CALL;
168         call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
169         call_msg.rm_call.cb_prog = program;
170         call_msg.rm_call.cb_vers = version;
171         xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
172             sendsz, XDR_ENCODE);
173         if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
174                 goto fooy;
175         }
176         cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
177         if (*sockp < 0) {
178                 int dontblock = 1;
179
180                 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
181                 if (*sockp < 0) {
182                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
183                         rpc_createerr.cf_error.re_errno = errno;
184                         goto fooy;
185                 }
186                 /* attempt to bind to priv port */
187                 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
188                 /* the sockets rpc controls are non-blocking */
189                 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
190                 cu->cu_closeit = TRUE;
191         } else {
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         register int *sockp;
212 {
213
214         return(clntudp_bufcreate(raddr, program, version, wait, sockp,
215             UDPMSGSIZE, UDPMSGSIZE));
216 }
217
218 static enum clnt_stat
219 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
220         register CLIENT *cl;            /* client handle */
221         u_long          proc;           /* procedure number */
222         xdrproc_t       xargs;          /* xdr routine for args */
223         caddr_t         argsp;          /* pointer to args */
224         xdrproc_t       xresults;       /* xdr routine for results */
225         caddr_t         resultsp;       /* pointer to results */
226         struct timeval  utimeout;       /* seconds to wait before giving up */
227 {
228         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
229         register XDR *xdrs;
230         register int outlen;
231         register int inlen;
232         struct sockaddr *sa;
233         int fromlen;
234         fd_set *fds, readfds;
235         struct sockaddr_in from;
236         struct rpc_msg reply_msg;
237         XDR reply_xdrs;
238         struct timeval time_waited, start, after, tmp1, tmp2, tv;
239         bool_t ok;
240         int nrefreshes = 2;     /* number of times to refresh cred */
241         struct timeval timeout;
242         socklen_t salen;
243
244         if (cu->cu_total.tv_usec == -1)
245                 timeout = utimeout;     /* use supplied timeout */
246         else
247                 timeout = cu->cu_total; /* use default timeout */
248
249         if (cu->cu_sock + 1 > FD_SETSIZE) {
250                 int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
251                 fds = (fd_set *)malloc(bytes);
252                 if (fds == NULL)
253                         return (cu->cu_error.re_status = RPC_CANTSEND);
254                 memset(fds, 0, bytes);
255         } else {
256                 fds = &readfds;
257                 FD_ZERO(fds);
258         }
259
260         if (cu->cu_connect && !cu->cu_connected) {
261                 if (connect(cu->cu_sock, (struct sockaddr *)&cu->cu_raddr,
262                     cu->cu_rlen) < 0) {
263                         cu->cu_error.re_errno = errno;
264                         if (fds != &readfds)
265                                 free(fds);
266                         return (cu->cu_error.re_status = RPC_CANTSEND);
267                 }
268                 cu->cu_connected = 1;
269         }
270         if (cu->cu_connected) {
271                 sa = NULL;
272                 salen = 0;
273         } else {
274                 sa = (struct sockaddr *)&cu->cu_raddr;
275                 salen = cu->cu_rlen;
276         }
277         timerclear(&time_waited);
278
279 call_again:
280         xdrs = &(cu->cu_outxdrs);
281         xdrs->x_op = XDR_ENCODE;
282         XDR_SETPOS(xdrs, cu->cu_xdrpos);
283         /*
284          * the transaction is the first thing in the out buffer
285          */
286         (*(u_short *)(cu->cu_outbuf))++;
287         if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
288             (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
289             (! (*xargs)(xdrs, argsp))) {
290                 if (fds != &readfds)
291                         free(fds);
292                 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
293         }
294         outlen = (int)XDR_GETPOS(xdrs);
295
296 send_again:
297         if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, sa, salen)
298             != outlen) {
299                 cu->cu_error.re_errno = errno;
300                 if (fds != &readfds)
301                         free(fds);
302                 return (cu->cu_error.re_status = RPC_CANTSEND);
303         }
304
305         /*
306          * Hack to provide rpc-based message passing
307          */
308         if (!timerisset(&timeout)) {
309                 if (fds != &readfds)
310                         free(fds);
311                 return (cu->cu_error.re_status = RPC_TIMEDOUT);
312         }
313         /*
314          * sub-optimal code appears here because we have
315          * some clock time to spare while the packets are in flight.
316          * (We assume that this is actually only executed once.)
317          */
318         reply_msg.acpted_rply.ar_verf = _null_auth;
319         reply_msg.acpted_rply.ar_results.where = resultsp;
320         reply_msg.acpted_rply.ar_results.proc = xresults;
321
322         gettimeofday(&start, NULL);
323         for (;;) {
324                 /* XXX we know the other bits are still clear */
325                 FD_SET(cu->cu_sock, fds);
326                 tv = cu->cu_wait;
327                 switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
328
329                 case 0:
330                         timeradd(&time_waited, &cu->cu_wait, &tmp1);
331                         time_waited = tmp1;
332                         if (timercmp(&time_waited, &timeout, <))
333                                 goto send_again;
334                         if (fds != &readfds)
335                                 free(fds);
336                         return (cu->cu_error.re_status = RPC_TIMEDOUT);
337
338                 case -1:
339                         if (errno == EINTR) {
340                                 gettimeofday(&after, NULL);
341                                 timersub(&after, &start, &tmp1);
342                                 timeradd(&time_waited, &tmp1, &tmp2);
343                                 time_waited = tmp2;
344                                 if (timercmp(&time_waited, &timeout, <))
345                                         continue;
346                                 if (fds != &readfds)
347                                         free(fds);
348                                 return (cu->cu_error.re_status = RPC_TIMEDOUT);
349                         }
350                         cu->cu_error.re_errno = errno;
351                         if (fds != &readfds)
352                                 free(fds);
353                         return (cu->cu_error.re_status = RPC_CANTRECV);
354                 }
355
356                 do {
357                         fromlen = sizeof(struct sockaddr);
358                         inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
359                                 (int) cu->cu_recvsz, 0,
360                                 (struct sockaddr *)&from, &fromlen);
361                 } while (inlen < 0 && errno == EINTR);
362                 if (inlen < 0) {
363                         if (errno == EWOULDBLOCK)
364                                 continue;
365                         cu->cu_error.re_errno = errno;
366                         if (fds != &readfds)
367                                 free(fds);
368                         return (cu->cu_error.re_status = RPC_CANTRECV);
369                 }
370                 if (inlen < sizeof(u_int32_t))
371                         continue;
372                 /* see if reply transaction id matches sent id */
373                 if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
374                         continue;
375                 /* we now assume we have the proper reply */
376                 break;
377         }
378
379         /*
380          * now decode and validate the response
381          */
382         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
383         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
384         /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
385         if (ok) {
386                 _seterr_reply(&reply_msg, &(cu->cu_error));
387                 if (cu->cu_error.re_status == RPC_SUCCESS) {
388                         if (! AUTH_VALIDATE(cl->cl_auth,
389                                 &reply_msg.acpted_rply.ar_verf)) {
390                                 cu->cu_error.re_status = RPC_AUTHERROR;
391                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
392                         }
393                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
394                                 xdrs->x_op = XDR_FREE;
395                                 (void)xdr_opaque_auth(xdrs,
396                                     &(reply_msg.acpted_rply.ar_verf));
397                         }
398                 }  /* end successful completion */
399                 else {
400                         /* maybe our credentials need to be refreshed ... */
401                         if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
402                                 nrefreshes--;
403                                 goto call_again;
404                         }
405                 }  /* end of unsuccessful completion */
406         }  /* end of valid reply message */
407         else {
408                 /*
409                  * It's possible for xdr_replymsg() to fail partway
410                  * through its attempt to decode the result from the
411                  * server. If this happens, it will leave the reply
412                  * structure partially populated with dynamically
413                  * allocated memory. (This can happen if someone uses
414                  * clntudp_bufcreate() to create a CLIENT handle and
415                  * specifies a receive buffer size that is too small.)
416                  * This memory must be free()ed to avoid a leak.
417                  */
418                 int op = reply_xdrs.x_op;
419                 reply_xdrs.x_op = XDR_FREE;
420                 xdr_replymsg(&reply_xdrs, &reply_msg);
421                 reply_xdrs.x_op = op;
422                 cu->cu_error.re_status = RPC_CANTDECODERES;
423         }
424         if (fds != &readfds)
425                 free(fds);
426         return (cu->cu_error.re_status);
427 }
428
429 static void
430 clntudp_geterr(cl, errp)
431         CLIENT *cl;
432         struct rpc_err *errp;
433 {
434         register 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(cl, xdr_res, res_ptr)
442         CLIENT *cl;
443         xdrproc_t xdr_res;
444         caddr_t res_ptr;
445 {
446         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
447         register XDR *xdrs = &(cu->cu_outxdrs);
448
449         xdrs->x_op = XDR_FREE;
450         return ((*xdr_res)(xdrs, res_ptr));
451 }
452
453 static void
454 clntudp_abort(/*h*/)
455         /*CLIENT *h;*/
456 {
457 }
458
459
460 static bool_t
461 clntudp_control(cl, request, info)
462         CLIENT *cl;
463         int request;
464         char *info;
465 {
466         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
467         register struct timeval *tv;
468         int len;
469
470         switch (request) {
471         case CLSET_FD_CLOSE:
472                 cu->cu_closeit = TRUE;
473                 break;
474         case CLSET_FD_NCLOSE:
475                 cu->cu_closeit = FALSE;
476                 break;
477         case CLSET_TIMEOUT:
478                 if (info == NULL)
479                         return(FALSE);
480                 tv = (struct timeval *)info;
481                 cu->cu_total.tv_sec = tv->tv_sec;
482                 cu->cu_total.tv_usec = tv->tv_usec;
483                 break;
484         case CLGET_TIMEOUT:
485                 if (info == NULL)
486                         return(FALSE);
487                 *(struct timeval *)info = cu->cu_total;
488                 break;
489         case CLSET_RETRY_TIMEOUT:
490                 if (info == NULL)
491                         return(FALSE);
492                 tv = (struct timeval *)info;
493                 cu->cu_wait.tv_sec = tv->tv_sec;
494                 cu->cu_wait.tv_usec = tv->tv_usec;
495                 break;
496         case CLGET_RETRY_TIMEOUT:
497                 if (info == NULL)
498                         return(FALSE);
499                 *(struct timeval *)info = cu->cu_wait;
500                 break;
501         case CLGET_SERVER_ADDR:
502                 if (info == NULL)
503                         return(FALSE);
504                 *(struct sockaddr_in *)info = cu->cu_raddr;
505                 break;
506         case CLGET_FD:
507                 if (info == NULL)
508                         return(FALSE);
509                 *(int *)info = cu->cu_sock;
510                 break;
511         case CLGET_XID:
512                 /*
513                  * use the knowledge that xid is the
514                  * first element in the call structure *.
515                  * This will get the xid of the PREVIOUS call
516                  */
517                 if (info == NULL)
518                         return(FALSE);
519                 *(u_long *)info = ntohl(*(u_long *)cu->cu_outbuf);
520                 break;
521         case CLSET_XID:
522                 /* This will set the xid of the NEXT call */
523                 if (info == NULL)
524                         return(FALSE);
525                 *(u_long *)cu->cu_outbuf =  htonl(*(u_long *)info - 1);
526                 /* decrement by 1 as clntudp_call() increments once */
527         case CLGET_VERS:
528                 /*
529                  * This RELIES on the information that, in the call body,
530                  * the version number field is the fifth field from the
531                  * begining of the RPC header. MUST be changed if the
532                  * call_struct is changed
533                  */
534                 if (info == NULL)
535                         return(FALSE);
536                 *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
537                                                 4 * BYTES_PER_XDR_UNIT));
538                 break;
539         case CLSET_VERS:
540                 if (info == NULL)
541                         return(FALSE);
542                 *(u_long *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
543                                 = htonl(*(u_long *)info);
544                 break;
545         case CLGET_PROG:
546                 /*
547                  * This RELIES on the information that, in the call body,
548                  * the program number field is the  field from the
549                  * begining of the RPC header. MUST be changed if the
550                  * call_struct is changed
551                  */
552                 if (info == NULL)
553                         return(FALSE);
554                 *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
555                                                 3 * BYTES_PER_XDR_UNIT));
556                 break;
557         case CLSET_PROG:
558                 if (info == NULL)
559                         return(FALSE);
560                 *(u_long *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
561                                 = htonl(*(u_long *)info);
562                 break;
563         case CLGET_LOCAL_ADDR:
564                 len = sizeof(struct sockaddr);
565                 if (getsockname(cu->cu_sock, (struct sockaddr *)info, &len) <0)
566                         return(FALSE);
567                 break;
568         case CLSET_CONNECT:
569                 cu->cu_connect = *(int *)(void *)info;
570                 break;
571         case CLGET_SVC_ADDR:
572         case CLSET_SVC_ADDR:
573         case CLSET_PUSH_TIMOD:
574         case CLSET_POP_TIMOD:
575         default:
576                 return (FALSE);
577         }
578         return (TRUE);
579 }
580
581 static void
582 clntudp_destroy(cl)
583         CLIENT *cl;
584 {
585         register struct cu_data *cu = (struct cu_data *)cl->cl_private;
586
587         if (cu->cu_closeit) {
588                 (void)_close(cu->cu_sock);
589         }
590         XDR_DESTROY(&(cu->cu_outxdrs));
591         mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
592         mem_free((caddr_t)cl, sizeof(CLIENT));
593 }