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

4.115
Last change on this file since ced84fc0 was ced84fc0, checked in by Jeffrey Hill <johill@…>, on Feb 23, 2015 at 5:27:05 PM

rpc: misaligned pointer dereference in clnt_udp.c line 363

closes #2248

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