Remove _THREAD_SAFE depenendancies. Create weakly associated stubs for
[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.4 2005/01/31 22:29:38 dillon 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(raddr, program, version, wait, sockp, sendsz, recvsz)
112         struct sockaddr_in *raddr;
113         u_long program;
114         u_long version;
115         struct timeval wait;
116         int *sockp;
117         u_int sendsz;
118         u_int recvsz;
119 {
120         CLIENT *cl;
121         struct cu_data *cu = NULL;
122         struct timeval now;
123         struct rpc_msg call_msg;
124         static u_int32_t disrupt;
125
126         if (disrupt == 0)
127                 disrupt = (u_int32_t)(long)raddr;
128
129         cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
130         if (cl == NULL) {
131                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
132                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
133                 rpc_createerr.cf_error.re_errno = errno;
134                 goto fooy;
135         }
136         sendsz = ((sendsz + 3) / 4) * 4;
137         recvsz = ((recvsz + 3) / 4) * 4;
138         cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
139         if (cu == NULL) {
140                 (void) fprintf(stderr, "clntudp_create: out of memory\n");
141                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
142                 rpc_createerr.cf_error.re_errno = errno;
143                 goto fooy;
144         }
145         cu->cu_outbuf = &cu->cu_inbuf[recvsz];
146
147         (void)gettimeofday(&now, (struct timezone *)0);
148         if (raddr->sin_port == 0) {
149                 u_short port;
150                 if ((port =
151                     pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
152                         goto fooy;
153                 }
154                 raddr->sin_port = htons(port);
155         }
156         cl->cl_ops = &udp_ops;
157         cl->cl_private = (caddr_t)cu;
158         cu->cu_raddr = *raddr;
159         cu->cu_rlen = sizeof (cu->cu_raddr);
160         cu->cu_wait = wait;
161         cu->cu_total.tv_sec = -1;
162         cu->cu_total.tv_usec = -1;
163         cu->cu_sendsz = sendsz;
164         cu->cu_recvsz = recvsz;
165         cu->cu_connect = FALSE;
166         cu->cu_connected = FALSE;
167         call_msg.rm_xid = (++disrupt) ^ 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 priv 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         cu->cu_sock = *sockp;
196         cl->cl_auth = authnone_create();
197         return (cl);
198 fooy:
199         if (cu)
200                 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
201         if (cl)
202                 mem_free((caddr_t)cl, sizeof(CLIENT));
203         return ((CLIENT *)NULL);
204 }
205
206 CLIENT *
207 clntudp_create(raddr, program, version, wait, sockp)
208         struct sockaddr_in *raddr;
209         u_long program;
210         u_long version;
211         struct timeval wait;
212         int *sockp;
213 {
214
215         return(clntudp_bufcreate(raddr, program, version, wait, sockp,
216             UDPMSGSIZE, UDPMSGSIZE));
217 }
218
219 static enum clnt_stat
220 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
221         CLIENT  *cl;            /* client handle */
222         u_long          proc;           /* procedure number */
223         xdrproc_t       xargs;          /* xdr routine for args */
224         caddr_t         argsp;          /* pointer to args */
225         xdrproc_t       xresults;       /* xdr routine for results */
226         caddr_t         resultsp;       /* pointer to results */
227         struct timeval  utimeout;       /* seconds to wait before giving up */
228 {
229         struct cu_data *cu = (struct cu_data *)cl->cl_private;
230         XDR *xdrs;
231         int outlen;
232         int inlen;
233         struct sockaddr *sa;
234         int fromlen;
235         fd_set *fds, readfds;
236         struct sockaddr_in from;
237         struct rpc_msg reply_msg;
238         XDR reply_xdrs;
239         struct timeval time_waited, start, after, tmp1, tmp2, tv;
240         bool_t ok;
241         int nrefreshes = 2;     /* number of times to refresh cred */
242         struct timeval timeout;
243         socklen_t salen;
244
245         if (cu->cu_total.tv_usec == -1)
246                 timeout = utimeout;     /* use supplied timeout */
247         else
248                 timeout = cu->cu_total; /* use default timeout */
249
250         if (cu->cu_sock + 1 > FD_SETSIZE) {
251                 int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
252                 fds = (fd_set *)malloc(bytes);
253                 if (fds == NULL)
254                         return (cu->cu_error.re_status = RPC_CANTSEND);
255                 memset(fds, 0, bytes);
256         } else {
257                 fds = &readfds;
258                 FD_ZERO(fds);
259         }
260
261         if (cu->cu_connect && !cu->cu_connected) {
262                 if (connect(cu->cu_sock, (struct sockaddr *)&cu->cu_raddr,
263                     cu->cu_rlen) < 0) {
264                         cu->cu_error.re_errno = errno;
265                         if (fds != &readfds)
266                                 free(fds);
267                         return (cu->cu_error.re_status = RPC_CANTSEND);
268                 }
269                 cu->cu_connected = 1;
270         }
271         if (cu->cu_connected) {
272                 sa = NULL;
273                 salen = 0;
274         } else {
275                 sa = (struct sockaddr *)&cu->cu_raddr;
276                 salen = cu->cu_rlen;
277         }
278         timerclear(&time_waited);
279
280 call_again:
281         xdrs = &(cu->cu_outxdrs);
282         xdrs->x_op = XDR_ENCODE;
283         XDR_SETPOS(xdrs, cu->cu_xdrpos);
284         /*
285          * the transaction is the first thing in the out buffer
286          */
287         (*(u_short *)(cu->cu_outbuf))++;
288         if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
289             (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
290             (! (*xargs)(xdrs, argsp))) {
291                 if (fds != &readfds)
292                         free(fds);
293                 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
294         }
295         outlen = (int)XDR_GETPOS(xdrs);
296
297 send_again:
298         if (_sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, sa, salen)
299             != outlen) {
300                 cu->cu_error.re_errno = errno;
301                 if (fds != &readfds)
302                         free(fds);
303                 return (cu->cu_error.re_status = RPC_CANTSEND);
304         }
305
306         /*
307          * Hack to provide rpc-based message passing
308          */
309         if (!timerisset(&timeout)) {
310                 if (fds != &readfds)
311                         free(fds);
312                 return (cu->cu_error.re_status = RPC_TIMEDOUT);
313         }
314         /*
315          * sub-optimal code appears here because we have
316          * some clock time to spare while the packets are in flight.
317          * (We assume that this is actually only executed once.)
318          */
319         reply_msg.acpted_rply.ar_verf = _null_auth;
320         reply_msg.acpted_rply.ar_results.where = resultsp;
321         reply_msg.acpted_rply.ar_results.proc = xresults;
322
323         gettimeofday(&start, NULL);
324         for (;;) {
325                 /* XXX we know the other bits are still clear */
326                 FD_SET(cu->cu_sock, fds);
327                 tv = cu->cu_wait;
328                 switch (_select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
329
330                 case 0:
331                         timeradd(&time_waited, &cu->cu_wait, &tmp1);
332                         time_waited = tmp1;
333                         if (timercmp(&time_waited, &timeout, <))
334                                 goto send_again;
335                         if (fds != &readfds)
336                                 free(fds);
337                         return (cu->cu_error.re_status = RPC_TIMEDOUT);
338
339                 case -1:
340                         if (errno == EINTR) {
341                                 gettimeofday(&after, NULL);
342                                 timersub(&after, &start, &tmp1);
343                                 timeradd(&time_waited, &tmp1, &tmp2);
344                                 time_waited = tmp2;
345                                 if (timercmp(&time_waited, &timeout, <))
346                                         continue;
347                                 if (fds != &readfds)
348                                         free(fds);
349                                 return (cu->cu_error.re_status = RPC_TIMEDOUT);
350                         }
351                         cu->cu_error.re_errno = errno;
352                         if (fds != &readfds)
353                                 free(fds);
354                         return (cu->cu_error.re_status = RPC_CANTRECV);
355                 }
356
357                 do {
358                         fromlen = sizeof(struct sockaddr);
359                         inlen = _recvfrom(cu->cu_sock, cu->cu_inbuf,
360                                 (int) cu->cu_recvsz, 0,
361                                 (struct sockaddr *)&from, &fromlen);
362                 } while (inlen < 0 && errno == EINTR);
363                 if (inlen < 0) {
364                         if (errno == EWOULDBLOCK)
365                                 continue;
366                         cu->cu_error.re_errno = errno;
367                         if (fds != &readfds)
368                                 free(fds);
369                         return (cu->cu_error.re_status = RPC_CANTRECV);
370                 }
371                 if (inlen < sizeof(u_int32_t))
372                         continue;
373                 /* see if reply transaction id matches sent id */
374                 if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
375                         continue;
376                 /* we now assume we have the proper reply */
377                 break;
378         }
379
380         /*
381          * now decode and validate the response
382          */
383         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
384         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
385         /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
386         if (ok) {
387                 _seterr_reply(&reply_msg, &(cu->cu_error));
388                 if (cu->cu_error.re_status == RPC_SUCCESS) {
389                         if (! AUTH_VALIDATE(cl->cl_auth,
390                                 &reply_msg.acpted_rply.ar_verf)) {
391                                 cu->cu_error.re_status = RPC_AUTHERROR;
392                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
393                         }
394                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
395                                 xdrs->x_op = XDR_FREE;
396                                 (void)xdr_opaque_auth(xdrs,
397                                     &(reply_msg.acpted_rply.ar_verf));
398                         }
399                 }  /* end successful completion */
400                 else {
401                         /* maybe our credentials need to be refreshed ... */
402                         if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
403                                 nrefreshes--;
404                                 goto call_again;
405                         }
406                 }  /* end of unsuccessful completion */
407         }  /* end of valid reply message */
408         else {
409                 /*
410                  * It's possible for xdr_replymsg() to fail partway
411                  * through its attempt to decode the result from the
412                  * server. If this happens, it will leave the reply
413                  * structure partially populated with dynamically
414                  * allocated memory. (This can happen if someone uses
415                  * clntudp_bufcreate() to create a CLIENT handle and
416                  * specifies a receive buffer size that is too small.)
417                  * This memory must be free()ed to avoid a leak.
418                  */
419                 int op = reply_xdrs.x_op;
420                 reply_xdrs.x_op = XDR_FREE;
421                 xdr_replymsg(&reply_xdrs, &reply_msg);
422                 reply_xdrs.x_op = op;
423                 cu->cu_error.re_status = RPC_CANTDECODERES;
424         }
425         if (fds != &readfds)
426                 free(fds);
427         return (cu->cu_error.re_status);
428 }
429
430 static void
431 clntudp_geterr(cl, errp)
432         CLIENT *cl;
433         struct rpc_err *errp;
434 {
435         struct cu_data *cu = (struct cu_data *)cl->cl_private;
436
437         *errp = cu->cu_error;
438 }
439
440
441 static bool_t
442 clntudp_freeres(cl, xdr_res, res_ptr)
443         CLIENT *cl;
444         xdrproc_t xdr_res;
445         caddr_t res_ptr;
446 {
447         struct cu_data *cu = (struct cu_data *)cl->cl_private;
448         XDR *xdrs = &(cu->cu_outxdrs);
449
450         xdrs->x_op = XDR_FREE;
451         return ((*xdr_res)(xdrs, res_ptr));
452 }
453
454 static void
455 clntudp_abort(/*h*/)
456         /*CLIENT *h;*/
457 {
458 }
459
460
461 static bool_t
462 clntudp_control(cl, request, info)
463         CLIENT *cl;
464         int request;
465         char *info;
466 {
467         struct cu_data *cu = (struct cu_data *)cl->cl_private;
468         struct timeval *tv;
469         int len;
470
471         switch (request) {
472         case CLSET_FD_CLOSE:
473                 cu->cu_closeit = TRUE;
474                 break;
475         case CLSET_FD_NCLOSE:
476                 cu->cu_closeit = FALSE;
477                 break;
478         case CLSET_TIMEOUT:
479                 if (info == NULL)
480                         return(FALSE);
481                 tv = (struct timeval *)info;
482                 cu->cu_total.tv_sec = tv->tv_sec;
483                 cu->cu_total.tv_usec = tv->tv_usec;
484                 break;
485         case CLGET_TIMEOUT:
486                 if (info == NULL)
487                         return(FALSE);
488                 *(struct timeval *)info = cu->cu_total;
489                 break;
490         case CLSET_RETRY_TIMEOUT:
491                 if (info == NULL)
492                         return(FALSE);
493                 tv = (struct timeval *)info;
494                 cu->cu_wait.tv_sec = tv->tv_sec;
495                 cu->cu_wait.tv_usec = tv->tv_usec;
496                 break;
497         case CLGET_RETRY_TIMEOUT:
498                 if (info == NULL)
499                         return(FALSE);
500                 *(struct timeval *)info = cu->cu_wait;
501                 break;
502         case CLGET_SERVER_ADDR:
503                 if (info == NULL)
504                         return(FALSE);
505                 *(struct sockaddr_in *)info = cu->cu_raddr;
506                 break;
507         case CLGET_FD:
508                 if (info == NULL)
509                         return(FALSE);
510                 *(int *)info = cu->cu_sock;
511                 break;
512         case CLGET_XID:
513                 /*
514                  * use the knowledge that xid is the
515                  * first element in the call structure *.
516                  * This will get the xid of the PREVIOUS call
517                  */
518                 if (info == NULL)
519                         return(FALSE);
520                 *(u_long *)info = ntohl(*(u_long *)cu->cu_outbuf);
521                 break;
522         case CLSET_XID:
523                 /* This will set the xid of the NEXT call */
524                 if (info == NULL)
525                         return(FALSE);
526                 *(u_long *)cu->cu_outbuf =  htonl(*(u_long *)info - 1);
527                 /* decrement by 1 as clntudp_call() increments once */
528         case CLGET_VERS:
529                 /*
530                  * This RELIES on the information that, in the call body,
531                  * the version number field is the fifth field from the
532                  * begining of the RPC header. MUST be changed if the
533                  * call_struct is changed
534                  */
535                 if (info == NULL)
536                         return(FALSE);
537                 *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
538                                                 4 * BYTES_PER_XDR_UNIT));
539                 break;
540         case CLSET_VERS:
541                 if (info == NULL)
542                         return(FALSE);
543                 *(u_long *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
544                                 = htonl(*(u_long *)info);
545                 break;
546         case CLGET_PROG:
547                 /*
548                  * This RELIES on the information that, in the call body,
549                  * the program number field is the  field from the
550                  * begining of the RPC header. MUST be changed if the
551                  * call_struct is changed
552                  */
553                 if (info == NULL)
554                         return(FALSE);
555                 *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
556                                                 3 * BYTES_PER_XDR_UNIT));
557                 break;
558         case CLSET_PROG:
559                 if (info == NULL)
560                         return(FALSE);
561                 *(u_long *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
562                                 = htonl(*(u_long *)info);
563                 break;
564         case CLGET_LOCAL_ADDR:
565                 len = sizeof(struct sockaddr);
566                 if (_getsockname(cu->cu_sock, (struct sockaddr *)info, &len) <0)
567                         return(FALSE);
568                 break;
569         case CLSET_CONNECT:
570                 cu->cu_connect = *(int *)(void *)info;
571                 break;
572         case CLGET_SVC_ADDR:
573         case CLSET_SVC_ADDR:
574         case CLSET_PUSH_TIMOD:
575         case CLSET_POP_TIMOD:
576         default:
577                 return (FALSE);
578         }
579         return (TRUE);
580 }
581
582 static void
583 clntudp_destroy(cl)
584         CLIENT *cl;
585 {
586         struct cu_data *cu = (struct cu_data *)cl->cl_private;
587
588         if (cu->cu_closeit) {
589                 (void)_close(cu->cu_sock);
590         }
591         XDR_DESTROY(&(cu->cu_outxdrs));
592         mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
593         mem_free((caddr_t)cl, sizeof(CLIENT));
594 }