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