source: rtems/cpukit/librpc/src/xdr/xdr_rec.c @ cd791626

4.104.114.95
Last change on this file since cd791626 was cd791626, checked in by Ralf Corsepius <ralf.corsepius@…>, on 08/01/08 at 16:44:07

Misc. ansifications. Add prototypes.

  • Property mode set to 100644
File size: 15.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#if defined(LIBC_SCCS) && !defined(lint)
30/*static char *sccsid = "from: @(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";*/
31/*static char *sccsid = "from: @(#)xdr_rec.c    2.2 88/08/01 4.0 RPCSRC";*/
32static char *rcsid = "$FreeBSD: src/lib/libc/xdr/xdr_rec.c,v 1.12 2000/01/19 06:12:32 wpaul Exp $";
33#endif
34
35/*
36 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
37 * layer above tcp (for rpc's use).
38 *
39 * Copyright (C) 1984, Sun Microsystems, Inc.
40 *
41 * These routines interface XDRSTREAMS to a tcp/ip connection.
42 * There is a record marking layer between the xdr stream
43 * and the tcp transport level.  A record is composed on one or more
44 * record fragments.  A record fragment is a thirty-two bit header followed
45 * by n bytes of data, where n is contained in the header.  The header
46 * is represented as a htonl(u_long).  Thegh order bit encodes
47 * whether or not the fragment is the last fragment of the record
48 * (1 => fragment is last, 0 => more fragments to follow.
49 * The other 31 bits encode the byte length of the fragment.
50 */
51
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <rpc/types.h>
56#include <rpc/xdr.h>
57#include <netinet/in.h>
58#include <unistd.h>   /* for lseek() */
59
60typedef struct rec_strm RECSTREAM;
61
62static u_int    fix_buf_size(u_int);
63static bool_t   flush_out(RECSTREAM *rstrm, bool_t eor);
64static bool_t   get_input_bytes(RECSTREAM *rstrm, caddr_t addr, int len);
65static bool_t   set_input_fragment(RECSTREAM *rstrm);
66static bool_t   skip_input_bytes(RECSTREAM *rstrm, long cnt);
67
68static bool_t   xdrrec_getlong(XDR *xdrs, long *lp);
69static bool_t   xdrrec_putlong(XDR *xdrs, const long *lp);
70static bool_t   xdrrec_getbytes(XDR *xdrs, caddr_t addr, u_int len);
71static bool_t   xdrrec_putbytes(XDR *xdrs, const char *addr, u_int len);
72static u_int    xdrrec_getpos(XDR *xdrs);
73static bool_t   xdrrec_setpos(XDR *xdrs, u_int pos);
74static int32_t *xdrrec_inline(XDR *xdrs, u_int len);
75static void     xdrrec_destroy(XDR *xdrs);
76
77static struct  xdr_ops xdrrec_ops = {
78        xdrrec_getlong,
79        xdrrec_putlong,
80        xdrrec_getbytes,
81        xdrrec_putbytes,
82        xdrrec_getpos,
83        xdrrec_setpos,
84        xdrrec_inline,
85        xdrrec_destroy
86};
87
88/*
89 * A record is composed of one or more record fragments.
90 * A record fragment is a two-byte header followed by zero to
91 * 2**32-1 bytes.  The header is treated as a long unsigned and is
92 * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
93 * are a byte count of the fragment.  The highest order bit is a boolean:
94 * 1 => this fragment is the last fragment of the record,
95 * 0 => this fragment is followed by more fragment(s).
96 *
97 * The fragment/record machinery is not general;  it is constructed to
98 * meet the needs of xdr and rpc based on tcp.
99 */
100
101#define LAST_FRAG ((u_int32_t)(1L << 31))
102
103struct rec_strm {
104        caddr_t tcp_handle;
105        caddr_t the_buffer;
106        /*
107         * out-goung bits
108         */
109        int (*writeit) (caddr_t, caddr_t, int);
110        caddr_t out_base;       /* output buffer (points to frag header) */
111        caddr_t out_finger;     /* next output position */
112        caddr_t out_boundry;    /* data cannot up to this address */
113        u_int32_t *frag_header; /* beginning of current fragment */
114        bool_t frag_sent;       /* true if buffer sent in middle of record */
115        /*
116         * in-coming bits
117         */
118        int (*readit) (caddr_t, caddr_t, int);
119        u_long in_size; /* fixed size of the input buffer */
120        caddr_t in_base;
121        caddr_t in_finger;      /* location of next byte to be had */
122        caddr_t in_boundry;     /* can read up to this location */
123        long fbtbc;             /* fragment bytes to be consumed */
124        bool_t last_frag;
125        u_int sendsize;
126        u_int recvsize;
127};
128
129
130/*
131 * Create an xdr handle for xdrrec
132 * xdrrec_create fills in xdrs.  Sendsize and recvsize are
133 * send and recv buffer sizes (0 => use default).
134 * tcp_handle is an opaque handle that is passed as the first parameter to
135 * the procedures readit and writeit.  Readit and writeit are read and
136 * write respectively.   They are like the system
137 * calls expect that they take an opaque handle rather than an fd.
138 */
139void
140xdrrec_create(
141        XDR *xdrs,
142        u_int sendsize,
143        u_int recvsize,
144        caddr_t tcp_handle,
145        int (*readit)(char*, char*, int),  /* like read, but pass it a tcp_handle, not sock */
146        int (*writeit)(char*, char*, int)  /* like write, but pass it a tcp_handle, not sock */
147)
148{
149        RECSTREAM *rstrm =
150                (RECSTREAM *)mem_alloc(sizeof(RECSTREAM));
151
152        if (rstrm == NULL) {
153                (void)fprintf(stderr, "xdrrec_create: out of memory\n");
154                /*
155                 *  This is bad.  Should rework xdrrec_create to
156                 *  return a handle, and in this case return NULL
157                 */
158                return;
159        }
160        /*
161         * adjust sizes and allocate buffer quad byte aligned
162         */
163        rstrm->sendsize = sendsize = fix_buf_size(sendsize);
164        rstrm->recvsize = recvsize = fix_buf_size(recvsize);
165        rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
166        if (rstrm->the_buffer == NULL) {
167                (void)fprintf(stderr, "xdrrec_create: out of memory\n");
168                return;
169        }
170        for (rstrm->out_base = rstrm->the_buffer;
171                (uintptr_t)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
172                rstrm->out_base++);
173        rstrm->in_base = rstrm->out_base + sendsize;
174        /*
175         * now the rest ...
176         */
177        xdrs->x_ops = &xdrrec_ops;
178        xdrs->x_private = (caddr_t)rstrm;
179        rstrm->tcp_handle = tcp_handle;
180        rstrm->readit = readit;
181        rstrm->writeit = writeit;
182        rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
183        rstrm->frag_header = (u_int32_t *)rstrm->out_base;
184        rstrm->out_finger += sizeof(u_int32_t);
185        rstrm->out_boundry += sendsize;
186        rstrm->frag_sent = FALSE;
187        rstrm->in_size = recvsize;
188        rstrm->in_boundry = rstrm->in_base;
189        rstrm->in_finger = (rstrm->in_boundry += recvsize);
190        rstrm->fbtbc = 0;
191        rstrm->last_frag = TRUE;
192}
193
194
195/*
196 * The reoutines defined below are the xdr ops which will go into the
197 * xdr handle filled in by xdrrec_create.
198 */
199
200static bool_t
201xdrrec_getlong(
202        XDR *xdrs,
203        long *lp)
204{
205        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
206        int32_t *buflp = (int32_t *)(rstrm->in_finger);
207        int32_t mylong;
208
209        /* first try the inline, fast case */
210        if ((rstrm->fbtbc >= sizeof(int32_t)) &&
211                (((intptr_t)rstrm->in_boundry - (intptr_t)buflp) >= sizeof(int32_t))) {
212                *lp = (long)ntohl((u_int32_t)(*buflp));
213                rstrm->fbtbc -= sizeof(int32_t);
214                rstrm->in_finger += sizeof(int32_t);
215        } else {
216                if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong, sizeof(int32_t)))
217                        return (FALSE);
218                *lp = (long)ntohl((u_int32_t)mylong);
219        }
220        return (TRUE);
221}
222
223static bool_t
224xdrrec_putlong(
225        XDR *xdrs,
226        const long *lp)
227{
228        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
229        int32_t *dest_lp = ((int32_t *)(rstrm->out_finger));
230
231        if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
232                /*
233                 * this case should almost never happen so the code is
234                 * inefficient
235                 */
236                rstrm->out_finger -= sizeof(int32_t);
237                rstrm->frag_sent = TRUE;
238                if (! flush_out(rstrm, FALSE))
239                        return (FALSE);
240                dest_lp = ((int32_t *)(rstrm->out_finger));
241                rstrm->out_finger += sizeof(int32_t);
242        }
243        *dest_lp = (int32_t)htonl((u_int32_t)(*lp));
244        return (TRUE);
245}
246
247static bool_t  /* must manage buffers, fragments, and records */
248xdrrec_getbytes(
249        XDR *xdrs,
250        caddr_t addr,
251        u_int len)
252{
253        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
254        int current;
255
256        while (len > 0) {
257                current = rstrm->fbtbc;
258                if (current == 0) {
259                        if (rstrm->last_frag)
260                                return (FALSE);
261                        if (! set_input_fragment(rstrm))
262                                return (FALSE);
263                        continue;
264                }
265                current = (len < current) ? len : current;
266                if (! get_input_bytes(rstrm, addr, current))
267                        return (FALSE);
268                addr += current;
269                rstrm->fbtbc -= current;
270                len -= current;
271        }
272        return (TRUE);
273}
274
275static bool_t
276xdrrec_putbytes(
277        XDR *xdrs,
278        const char *addr,
279        u_int len)
280{
281        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
282        long current;
283
284        while (len > 0) {
285                current = (intptr_t)rstrm->out_boundry -
286                        (intptr_t)rstrm->out_finger;
287                current = (len < current) ? len : current;
288                memcpy(rstrm->out_finger, addr, current);
289                rstrm->out_finger += current;
290                addr += current;
291                len -= current;
292                if (rstrm->out_finger == rstrm->out_boundry) {
293                        rstrm->frag_sent = TRUE;
294                        if (! flush_out(rstrm, FALSE))
295                                return (FALSE);
296                }
297        }
298        return (TRUE);
299}
300
301static u_int
302xdrrec_getpos(
303        XDR *xdrs)
304{
305        RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
306        long pos;
307
308        pos = lseek((intptr_t)rstrm->tcp_handle, (off_t) 0, 1);
309        if (pos != -1)
310                switch (xdrs->x_op) {
311
312                case XDR_ENCODE:
313                        pos += rstrm->out_finger - rstrm->out_base;
314                        break;
315
316                case XDR_DECODE:
317                        pos -= rstrm->in_boundry - rstrm->in_finger;
318                        break;
319
320                default:
321                        pos = -1;
322                        break;
323                }
324        return ((u_int) pos);
325}
326
327static bool_t
328xdrrec_setpos(
329        XDR *xdrs,
330        u_int pos)
331{
332        RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
333        u_int currpos = xdrrec_getpos(xdrs);
334        int delta = currpos - pos;
335        caddr_t newpos;
336
337        if ((int)currpos != -1)
338                switch (xdrs->x_op) {
339
340                case XDR_ENCODE:
341                        newpos = rstrm->out_finger - delta;
342                        if ((newpos > (caddr_t)(rstrm->frag_header)) &&
343                                (newpos < rstrm->out_boundry)) {
344                                rstrm->out_finger = newpos;
345                                return (TRUE);
346                        }
347                        break;
348
349                case XDR_DECODE:
350                        newpos = rstrm->in_finger - delta;
351                        if ((delta < (int)(rstrm->fbtbc)) &&
352                                (newpos <= rstrm->in_boundry) &&
353                                (newpos >= rstrm->in_base)) {
354                                rstrm->in_finger = newpos;
355                                rstrm->fbtbc -= delta;
356                                return (TRUE);
357                        }
358                        break;
359                case XDR_FREE:  /* to avoid warning */
360                        break;
361                }
362        return (FALSE);
363}
364
365static int32_t *
366xdrrec_inline(
367        XDR *xdrs,
368        u_int len)
369{
370        RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
371        int32_t * buf = NULL;
372
373        switch (xdrs->x_op) {
374
375        case XDR_ENCODE:
376                if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
377                        buf = (int32_t *) rstrm->out_finger;
378                        rstrm->out_finger += len;
379                }
380                break;
381
382        case XDR_DECODE:
383                if ((len <= rstrm->fbtbc) &&
384                        ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
385                        buf = (int32_t *) rstrm->in_finger;
386                        rstrm->fbtbc -= len;
387                        rstrm->in_finger += len;
388                }
389                break;
390        case XDR_FREE:  /* to avoid warning */
391                break;
392        }
393        return (buf);
394}
395
396static void
397xdrrec_destroy(
398        XDR *xdrs)
399{
400        RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
401
402        mem_free(rstrm->the_buffer,
403                rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
404        mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
405}
406
407
408/*
409 * Exported routines to manage xdr records
410 */
411
412/*
413 * Before reading (deserializing from the stream, one should always call
414 * this procedure to guarantee proper record alignment.
415 */
416bool_t
417xdrrec_skiprecord(
418        XDR *xdrs)
419{
420        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
421
422        while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
423                if (! skip_input_bytes(rstrm, rstrm->fbtbc))
424                        return (FALSE);
425                rstrm->fbtbc = 0;
426                if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
427                        return (FALSE);
428        }
429        rstrm->last_frag = FALSE;
430        return (TRUE);
431}
432
433/*
434 * Look ahead fuction.
435 * Returns TRUE iff there is no more input in the buffer
436 * after consuming the rest of the current record.
437 */
438bool_t
439xdrrec_eof(
440        XDR *xdrs)
441{
442        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
443
444        while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
445                if (! skip_input_bytes(rstrm, rstrm->fbtbc))
446                        return (TRUE);
447                rstrm->fbtbc = 0;
448                if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
449                        return (TRUE);
450        }
451        if (rstrm->in_finger == rstrm->in_boundry)
452                return (TRUE);
453        return (FALSE);
454}
455
456/*
457 * The client must tell the package when an end-of-record has occurred.
458 * The second paraemters tells whether the record should be flushed to the
459 * (output) tcp stream.  (This let's the package support batched or
460 * pipelined procedure calls.)  TRUE => immmediate flush to tcp connection.
461 */
462bool_t
463xdrrec_endofrecord(
464        XDR *xdrs,
465        bool_t sendnow)
466{
467        RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
468        u_long len;  /* fragment length */
469
470        if (sendnow || rstrm->frag_sent ||
471                ((uintptr_t)rstrm->out_finger + sizeof(u_int32_t) >=
472                (uintptr_t)rstrm->out_boundry)) {
473                rstrm->frag_sent = FALSE;
474                return (flush_out(rstrm, TRUE));
475        }
476        len = (uintptr_t)(rstrm->out_finger) - (uintptr_t)(rstrm->frag_header) -
477           sizeof(u_int32_t);
478        *(rstrm->frag_header) = htonl((u_int32_t)len | LAST_FRAG);
479        rstrm->frag_header = (u_int32_t *)rstrm->out_finger;
480        rstrm->out_finger += sizeof(u_int32_t);
481        return (TRUE);
482}
483
484
485/*
486 * Internal useful routines
487 */
488static bool_t
489flush_out(
490        RECSTREAM *rstrm,
491        bool_t eor)
492{
493        u_int32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
494        u_int32_t len = (uintptr_t)(rstrm->out_finger) -
495                (uintptr_t)(rstrm->frag_header) - sizeof(u_int32_t);
496
497        *(rstrm->frag_header) = htonl(len | eormask);
498        len = (uintptr_t)(rstrm->out_finger) - (uintptr_t)(rstrm->out_base);
499        if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
500                != (int)len)
501                return (FALSE);
502        rstrm->frag_header = (u_int32_t *)rstrm->out_base;
503        rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(u_int32_t);
504        return (TRUE);
505}
506
507static bool_t  /* knows nothing about records!  Only about input buffers */
508fill_input_buf(
509        RECSTREAM *rstrm)
510{
511        caddr_t where;
512        u_long i;
513        long len;
514
515        where = rstrm->in_base;
516        i = (uintptr_t)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
517        where += i;
518        len = rstrm->in_size - i;
519        if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
520                return (FALSE);
521        rstrm->in_finger = where;
522        where += len;
523        rstrm->in_boundry = where;
524        return (TRUE);
525}
526
527static bool_t  /* knows nothing about records!  Only about input buffers */
528get_input_bytes(
529        RECSTREAM *rstrm,
530        caddr_t addr,
531        int len)
532{
533        long current;
534
535        while (len > 0) {
536                current = (intptr_t)rstrm->in_boundry - (intptr_t)rstrm->in_finger;
537                if (current == 0) {
538                        if (! fill_input_buf(rstrm))
539                                return (FALSE);
540                        continue;
541                }
542                current = (len < current) ? len : current;
543                memcpy(addr, rstrm->in_finger, current);
544                rstrm->in_finger += current;
545                addr += current;
546                len -= current;
547        }
548        return (TRUE);
549}
550
551static bool_t  /* next two bytes of the input stream are treated as a header */
552set_input_fragment(
553        RECSTREAM *rstrm)
554{
555        u_int32_t header;
556
557        if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
558                return (FALSE);
559        header = (long)ntohl(header);
560        rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
561        /*
562         * Sanity check. Try not to accept wildly incorrect
563         * record sizes. Unfortunately, the only record size
564         * we can positively identify as being 'wildly incorrect'
565         * is zero. Ridiculously large record sizes may look wrong,
566         * but we don't have any way to be certain that they aren't
567         * what the client actually intended to send us.
568         */
569        if (header == 0)
570                return(FALSE);
571        rstrm->fbtbc = header & (~LAST_FRAG);
572        return (TRUE);
573}
574
575static bool_t  /* consumes input bytes; knows nothing about records! */
576skip_input_bytes(
577        RECSTREAM *rstrm,
578        long cnt)
579{
580        long current;
581
582        while (cnt > 0) {
583                current = (intptr_t)rstrm->in_boundry - (intptr_t)rstrm->in_finger;
584                if (current == 0) {
585                        if (! fill_input_buf(rstrm))
586                                return (FALSE);
587                        continue;
588                }
589                current = (cnt < current) ? cnt : current;
590                rstrm->in_finger += current;
591                cnt -= current;
592        }
593        return (TRUE);
594}
595
596static u_int
597fix_buf_size(
598        u_int s)
599{
600
601        if (s < 100)
602                s = 4000;
603        return (RNDUP(s));
604}
Note: See TracBrowser for help on using the repository browser.