source: rtems/cpukit/librpc/src/rpc/clnt_tcp.c

Last change on this file was e069f7fe, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 10, 2018 at 4:27:10 AM

rpc: Use configuration header file

Update #3375.

  • Property mode set to 100644
File size: 15.2 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_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";*/
34/*static char *sccsid = "from: @(#)clnt_tcp.c   2.2 88/08/01 4.0 RPCSRC";*/
35static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_tcp.c,v 1.14 2000/01/27 23:06:36 jasone Exp $";
36#endif
37
38/*
39 * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
40 *
41 * Copyright (C) 1984, Sun Microsystems, Inc.
42 *
43 * TCP based RPC supports 'batched calls'.
44 * A sequence of calls may be batched-up in a send buffer.  The rpc call
45 * return immediately to the client even though the call was not necessarily
46 * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
47 * the rpc timeout value is zero (see clnt.h, rpc).
48 *
49 * Clients should NOT casually batch calls that in fact return results; that is,
50 * the server side should be aware that a call is batched and not produce any
51 * return message.  Batched calls that produce many result messages can
52 * deadlock (netlock) the client and the server....
53 *
54 * Now go hang yourself.
55 */
56
57#ifdef HAVE_CONFIG_H
58#include "config.h"
59#endif
60
61#include <stdio.h>
62#include <stdlib.h>
63#include <unistd.h>
64#include <string.h>
65#include <rpc/rpc.h>
66#include <sys/param.h>
67#include <sys/socket.h>
68#include <netdb.h>
69#include <errno.h>
70#include <rpc/pmap_clnt.h>
71#include <sys/select.h>
72
73#define MCALL_MSG_SIZE 24
74
75static int      readtcp(char *, char*, int);
76static int      writetcp(char *, char*, int);
77
78static enum clnt_stat   clnttcp_call(CLIENT *, rpcproc_t, xdrproc_t, void*, xdrproc_t, void*, struct timeval);
79static void             clnttcp_abort(void);
80static void             clnttcp_geterr(CLIENT *, struct rpc_err*);
81static bool_t           clnttcp_freeres(CLIENT *, xdrproc_t, void*);
82static bool_t           clnttcp_control(CLIENT *, int, char *);
83static void             clnttcp_destroy(CLIENT *);
84
85static struct clnt_ops tcp_ops = {
86        clnttcp_call,
87        clnttcp_abort,
88        clnttcp_geterr,
89        clnttcp_freeres,
90        clnttcp_destroy,
91        clnttcp_control
92};
93
94struct ct_data {
95        int             ct_sock;
96        bool_t          ct_closeit;     /* close it on destroy */
97        struct timeval  ct_wait;        /* wait interval in milliseconds */
98        bool_t          ct_waitset;       /* wait set by clnt_control? */
99        struct sockaddr_in ct_addr;
100        struct rpc_err  ct_error;
101        union {
102                char    ct_mcallc[MCALL_MSG_SIZE];      /* marshalled callmsg */
103                u_int32_t ct_mcalli;
104        } ct_u;
105        u_int           ct_mpos;                        /* pos after marshal */
106        XDR             ct_xdrs;        /* XDR stream */
107};
108
109/*
110 * Create a client handle for a tcp/ip connection.
111 * If *sockp<0, *sockp is set to a newly created TCP socket and it is
112 * connected to raddr.  If *sockp non-negative then
113 * raddr is ignored.  The rpc/tcp package does buffering
114 * similar to stdio, so the client must pick send and receive buffer sizes,];
115 * 0 => use the default.
116 * If raddr->sin_port is 0, then a binder on the remote machine is
117 * consulted for the right port number.
118 * NB: *sockp is copied into a private area.
119 * NB: It is the clients responsibility to close *sockp.
120 * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
121 * something more useful.
122 */
123CLIENT *
124clnttcp_create(
125        struct sockaddr_in *raddr,
126        u_long prog,            /* program number */
127        u_long vers,            /* version number */
128        int *sockp,
129        u_int sendsz,
130        u_int recvsz)
131{
132        CLIENT *h;
133        struct ct_data *ct = NULL;      /* client handle */
134        struct timeval now;
135        struct rpc_msg call_msg;
136        static uintptr_t disrupt;
137
138        if (disrupt == 0)
139                disrupt = (uintptr_t)raddr;
140
141        h  = (CLIENT *)mem_alloc(sizeof(*h));
142        if (h == NULL) {
143                (void)fprintf(stderr, "clnttcp_create: out of memory\n");
144                rpc_createerr.cf_stat = RPC_SYSTEMERROR;
145                rpc_createerr.cf_error.re_errno = errno;
146                goto fooy;
147        }
148        ct = (struct ct_data *)mem_alloc(sizeof (*ct));
149        if (ct == NULL) {
150                (void)fprintf(stderr, "clnttcp_create: out of memory\n");
151                rpc_createerr.cf_stat = RPC_SYSTEMERROR;
152                rpc_createerr.cf_error.re_errno = errno;
153                goto fooy;
154        }
155
156        /*
157         * If no port number given ask the pmap for one
158         */
159        if (raddr->sin_port == 0) {
160                u_short port;
161                if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
162                        mem_free((caddr_t)ct, sizeof(struct ct_data));
163                        mem_free((caddr_t)h, sizeof(CLIENT));
164                        return ((CLIENT *)NULL);
165                }
166                raddr->sin_port = htons(port);
167        }
168
169        /*
170         * If no socket given, open one
171         */
172        if (*sockp < 0) {
173                *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
174                (void)bindresvport(*sockp, (struct sockaddr_in *)0);
175                if ((*sockp < 0)
176                    || (connect(*sockp, (struct sockaddr *)raddr,
177                    sizeof(*raddr)) < 0)) {
178                        rpc_createerr.cf_stat = RPC_SYSTEMERROR;
179                        rpc_createerr.cf_error.re_errno = errno;
180                        if (*sockp != -1)
181                                (void)_RPC_close(*sockp);
182                        goto fooy;
183                }
184                ct->ct_closeit = TRUE;
185        } else {
186                ct->ct_closeit = FALSE;
187        }
188
189        /*
190         * Set up private data struct
191         */
192        ct->ct_sock = *sockp;
193        ct->ct_wait.tv_usec = 0;
194        ct->ct_waitset = FALSE;
195        ct->ct_addr = *raddr;
196
197        /*
198         * Initialize call message
199         */
200        (void)gettimeofday(&now, (struct timezone *)0);
201        call_msg.rm_xid = (++disrupt) ^ getpid() ^ now.tv_sec ^ now.tv_usec;
202        call_msg.rm_direction = CALL;
203        call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
204        call_msg.rm_call.cb_prog = prog;
205        call_msg.rm_call.cb_vers = vers;
206
207        /*
208         * pre-serialize the static part of the call msg and stash it away
209         */
210        xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
211            XDR_ENCODE);
212        if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
213                if (ct->ct_closeit) {
214                        (void)_RPC_close(*sockp);
215                }
216                goto fooy;
217        }
218        ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
219        XDR_DESTROY(&(ct->ct_xdrs));
220
221        /*
222         * Create a client handle which uses xdrrec for serialization
223         * and authnone for authentication.
224         */
225        xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
226            (caddr_t)ct, readtcp, writetcp);
227        h->cl_ops = &tcp_ops;
228        h->cl_private = (caddr_t) ct;
229        h->cl_auth = authnone_create();
230        return (h);
231
232fooy:
233        /*
234         * Something goofed, free stuff and barf
235         */
236        if (ct)
237                mem_free((caddr_t)ct, sizeof(struct ct_data));
238        if (h)
239                mem_free((caddr_t)h, sizeof(CLIENT));
240        return ((CLIENT *)NULL);
241}
242
243static enum clnt_stat
244clnttcp_call(
245        CLIENT *h,
246        rpcproc_t proc,
247        xdrproc_t xdr_args,
248        void *args_ptr,
249        xdrproc_t xdr_results,
250        void *results_ptr,
251        struct timeval timeout)
252{
253        struct ct_data *ct = (struct ct_data *) h->cl_private;
254        XDR *xdrs = &(ct->ct_xdrs);
255        struct rpc_msg reply_msg;
256        u_int32_t x_id;
257        u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli;      /* yuk */
258        bool_t shipnow;
259        int refreshes = 2;
260
261        if (!ct->ct_waitset) {
262                ct->ct_wait = timeout;
263        }
264
265        shipnow =
266            (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
267            && timeout.tv_usec == 0) ? FALSE : TRUE;
268
269call_again:
270        xdrs->x_op = XDR_ENCODE;
271        ct->ct_error.re_status = RPC_SUCCESS;
272        x_id = ntohl(--(*msg_x_id));
273        if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
274            (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
275            (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
276            (! (*xdr_args)(xdrs, args_ptr))) {
277                if (ct->ct_error.re_status == RPC_SUCCESS)
278                        ct->ct_error.re_status = RPC_CANTENCODEARGS;
279                (void)xdrrec_endofrecord(xdrs, TRUE);
280                return (ct->ct_error.re_status);
281        }
282        if (! xdrrec_endofrecord(xdrs, shipnow)) {
283                return (ct->ct_error.re_status = RPC_CANTSEND);
284        }
285        if (! shipnow) {
286                return (RPC_SUCCESS);
287        }
288        /*
289         * Hack to provide rpc-based message passing
290         */
291        if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
292                return(ct->ct_error.re_status = RPC_TIMEDOUT);
293        }
294
295
296        /*
297         * Keep receiving until we get a valid transaction id
298         */
299        xdrs->x_op = XDR_DECODE;
300        while (TRUE) {
301                reply_msg.acpted_rply.ar_verf = _null_auth;
302                reply_msg.acpted_rply.ar_results.where = NULL;
303                reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
304                if (! xdrrec_skiprecord(xdrs))
305                        return (ct->ct_error.re_status);
306                /* now decode and validate the response header */
307                if (! xdr_replymsg(xdrs, &reply_msg)) {
308                        if (ct->ct_error.re_status == RPC_SUCCESS)
309                                continue;
310                        return (ct->ct_error.re_status);
311                }
312                if (reply_msg.rm_xid == x_id)
313                        break;
314        }
315
316        /*
317         * process header
318         */
319        _seterr_reply(&reply_msg, &(ct->ct_error));
320        if (ct->ct_error.re_status == RPC_SUCCESS) {
321                if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
322                        ct->ct_error.re_status = RPC_AUTHERROR;
323                        ct->ct_error.re_why = AUTH_INVALIDRESP;
324                } else if (! (*xdr_results)(xdrs, results_ptr)) {
325                        if (ct->ct_error.re_status == RPC_SUCCESS)
326                                ct->ct_error.re_status = RPC_CANTDECODERES;
327                }
328                /* free verifier ... */
329                if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
330                        xdrs->x_op = XDR_FREE;
331                        (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
332                }
333        }  /* end successful completion */
334        else {
335                /* maybe our credentials need to be refreshed ... */
336                if (refreshes-- && AUTH_REFRESH(h->cl_auth))
337                        goto call_again;
338        }  /* end of unsuccessful completion */
339        return (ct->ct_error.re_status);
340}
341
342static void
343clnttcp_geterr(
344        CLIENT *h,
345        struct rpc_err *errp)
346{
347        struct ct_data *ct;
348
349        ct = (struct ct_data *) h->cl_private;
350        *errp = ct->ct_error;
351}
352
353static bool_t
354clnttcp_freeres(
355        CLIENT *cl,
356        xdrproc_t xdr_res,
357        void *res_ptr)
358{
359        struct ct_data *ct;
360        XDR *xdrs;
361
362        ct = (struct ct_data *)cl->cl_private;
363        xdrs = &(ct->ct_xdrs);
364       
365        xdrs->x_op = XDR_FREE;
366        return ((*xdr_res)(xdrs, res_ptr));
367}
368
369static void
370clnttcp_abort(void)
371{
372}
373
374
375static bool_t
376clnttcp_control(
377        CLIENT *cl,
378        int request,
379        char *info)
380{
381        struct ct_data *ct;
382        struct timeval *tv;
383        socklen_t len;
384       
385        ct = (struct ct_data *)cl->cl_private;
386
387        switch (request) {
388        case CLSET_FD_CLOSE:
389                ct->ct_closeit = TRUE;
390                break;
391        case CLSET_FD_NCLOSE:
392                ct->ct_closeit = FALSE;
393                break;
394        case CLSET_TIMEOUT:
395                if (info == NULL)
396                        return(FALSE);
397                tv = (struct timeval *)info;
398                ct->ct_wait.tv_sec = tv->tv_sec;
399                ct->ct_wait.tv_usec = tv->tv_usec;
400                ct->ct_waitset = TRUE;
401                break;
402        case CLGET_TIMEOUT:
403                if (info == NULL)
404                        return(FALSE);
405                *(struct timeval *)info = ct->ct_wait;
406                break;
407        case CLGET_SERVER_ADDR:
408                if (info == NULL)
409                        return(FALSE);
410                *(struct sockaddr_in *)info = ct->ct_addr;
411                break;
412        case CLGET_FD:
413                if (info == NULL)
414                        return(FALSE);
415                *(int *)info = ct->ct_sock;
416                break;
417        case CLGET_XID:
418                /*
419                 * use the knowledge that xid is the
420                 * first element in the call structure
421                 * This will get the xid of the PREVIOUS call
422                 */
423                if (info == NULL)
424                        return(FALSE);
425                *(u_int32_t *)info =
426                        ntohl(*(u_int32_t *)&ct->ct_u.ct_mcalli);
427                break;
428        case CLSET_XID:
429                /* This will set the xid of the NEXT call */
430                if (info == NULL)
431                        return(FALSE);
432                *(u_int32_t *)&ct->ct_u.ct_mcalli =
433                        htonl(*((u_int32_t *)info) + 1);
434                /* decrement by 1 as clnttcp_call() increments once */
435        case CLGET_VERS:
436                /*
437                 * This RELIES on the information that, in the call body,
438                 * the version number field is the fifth field from the
439                 * begining of the RPC header. MUST be changed if the
440                 * call_struct is changed
441                 */
442                if (info == NULL)
443                        return(FALSE);
444                *(u_int32_t *)info =
445                        ntohl(*(u_int32_t *)(ct->ct_u.ct_mcallc +
446                                                4 * BYTES_PER_XDR_UNIT));
447                break;
448        case CLSET_VERS:
449                if (info == NULL)
450                        return(FALSE);
451                *(u_int32_t *)(ct->ct_u.ct_mcallc +
452                        4 * BYTES_PER_XDR_UNIT) =
453                        htonl(*(u_int32_t *)info);
454                break;
455
456        case CLGET_PROG:
457                /*
458                 * This RELIES on the information that, in the call body,
459                 * the program number field is the fourth field from the
460                 * begining of the RPC header. MUST be changed if the
461                 * call_struct is changed
462                 */
463                if (info == NULL)
464                        return(FALSE);
465                *(u_int32_t *)info = ntohl(*(u_int32_t *)(ct->ct_u.ct_mcallc +
466                                                3 * BYTES_PER_XDR_UNIT));
467                break;
468
469        case CLSET_PROG:
470                if (info == NULL)
471                        return(FALSE);
472                *(u_int32_t *)(ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT)
473                                = htonl(*(u_int32_t *)info);
474                break;
475
476        case CLGET_LOCAL_ADDR:
477                len = sizeof(struct sockaddr);
478                if (getsockname(ct->ct_sock, (struct sockaddr *)info, &len) <0)
479                        return(FALSE);
480                break;
481
482        case CLGET_RETRY_TIMEOUT:
483        case CLSET_RETRY_TIMEOUT:
484        case CLGET_SVC_ADDR:
485        case CLSET_SVC_ADDR:
486        case CLSET_PUSH_TIMOD:
487        case CLSET_POP_TIMOD:
488        default:
489                return (FALSE);
490        }
491        return (TRUE);
492}
493
494
495static void
496clnttcp_destroy(
497        CLIENT *h)
498{
499        struct ct_data *ct =
500            (struct ct_data *) h->cl_private;
501
502        if (ct->ct_closeit) {
503                (void)_RPC_close(ct->ct_sock);
504        }
505        XDR_DESTROY(&(ct->ct_xdrs));
506        mem_free(ct, sizeof(struct ct_data));
507        mem_free(h, sizeof(CLIENT));
508}
509
510/*
511 * Interface between xdr serializer and tcp connection.
512 * Behaves like the system calls, read & write, but keeps some error state
513 * around for the rpc level.
514 */
515static int
516readtcp(
517        char *_ct,
518        char *buf,
519        int len)
520{
521        struct ct_data *ct = (struct ct_data*) _ct;
522        fd_set *fds, readfds;
523        struct timeval start, after, duration, delta, tmp, tv;
524        int r, save_errno;
525
526        if (len == 0)
527                return (0);
528
529        if (ct->ct_sock + 1 > FD_SETSIZE) {
530                int bytes = howmany(ct->ct_sock + 1, NFDBITS) * sizeof(fd_mask);
531                fds = (fd_set *)malloc(bytes);
532                if (fds == NULL)
533                        return (-1);
534                memset(fds, 0, bytes);
535        } else {
536                fds = &readfds;
537                FD_ZERO(fds);
538        }
539
540        gettimeofday(&start, NULL);
541        delta = ct->ct_wait;
542        while (TRUE) {
543                /* XXX we know the other bits are still clear */
544                FD_SET(ct->ct_sock, fds);
545                tv = delta;     /* in case select writes back */
546                r = select(ct->ct_sock+1, fds, NULL, NULL, &tv);
547                save_errno = errno;
548
549                gettimeofday(&after, NULL);
550                timersub(&start, &after, &duration);
551                timersub(&ct->ct_wait, &duration, &tmp);
552                delta = tmp;
553                if (delta.tv_sec < 0 || !timerisset(&delta))
554                        r = 0;
555
556                switch (r) {
557                case 0:
558                        if (fds != &readfds)
559                                free(fds);
560                        ct->ct_error.re_status = RPC_TIMEDOUT;
561                        return (-1);
562
563                case -1:
564                        if (errno == EINTR)
565                                continue;
566                        if (fds != &readfds)
567                                free(fds);
568                        ct->ct_error.re_status = RPC_CANTRECV;
569                        ct->ct_error.re_errno = save_errno;
570                        return (-1);
571                }
572                break;
573        }
574        switch (len = _RPC_read(ct->ct_sock, buf, len)) {
575
576        case 0:
577                /* premature eof */
578                ct->ct_error.re_errno = ECONNRESET;
579                ct->ct_error.re_status = RPC_CANTRECV;
580                len = -1;  /* it's really an error */
581                break;
582
583        case -1:
584                ct->ct_error.re_errno = errno;
585                ct->ct_error.re_status = RPC_CANTRECV;
586                break;
587        }
588        return (len);
589}
590
591static int
592writetcp(
593        char *_ct,
594        char *buf,
595        int len)
596{
597        struct ct_data *ct = (struct ct_data *) _ct;
598        int i, cnt;
599
600        for (cnt = len; cnt > 0; cnt -= i, buf += i) {
601                if ((i = _RPC_write(ct->ct_sock, buf, cnt)) == -1) {
602                        ct->ct_error.re_errno = errno;
603                        ct->ct_error.re_status = RPC_CANTSEND;
604                        return (-1);
605                }
606        }
607        return (len);
608}
Note: See TracBrowser for help on using the repository browser.