source: rtems/c/src/librpc/src/rpc/clnt_tcp.c @ 57cfaad

4.104.114.84.95
Last change on this file since 57cfaad was 57cfaad, checked in by Joel Sherrill <joel.sherrill@…>, on 10/31/00 at 16:39:06

2000-10-30 Joel Sherrill <joel@…>

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