source: rtems/cpukit/librpc/src/rpc/clnt_tcp.c @ 8f926f1

4.104.114.9
Last change on this file since 8f926f1 was 8f926f1, checked in by Ralf Corsepius <ralf.corsepius@…>, on Aug 1, 2008 at 3:47:13 PM

Add missing prototypes.

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