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

4.115
Last change on this file since 33a105fb was 37da47a, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/28/10 at 02:40:16

Add HAVE_CONFIG_H support to let files receive configure defines.

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