source: rtems/cpukit/librpc/src/rpc/clnt_udp.c @ e069f7fe

5
Last change on this file since e069f7fe was e069f7fe, checked in by Sebastian Huber <sebastian.huber@…>, on 09/10/18 at 04:27:10

rpc: Use configuration header file

Update #3375.

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