]>
git.saurik.com Git - apple/libinfo.git/blob - rpc.subproj/xdr_rec.c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 /* $NetBSD: xdr_rec.c,v 1.18 2000/07/06 03:10:35 christos Exp $ */
28 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
29 * unrestricted use provided that this legend is included on all tape
30 * media and as a part of the software program in whole or part. Users
31 * may copy or modify Sun RPC without charge, but are not authorized
32 * to license or distribute it to anyone else except as part of a product or
33 * program developed by the user.
35 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
36 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
37 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
39 * Sun RPC is provided with no support and without any obligation on the
40 * part of Sun Microsystems, Inc. to assist in its use, correction,
41 * modification or enhancement.
43 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
44 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
45 * OR ANY PART THEREOF.
47 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
48 * or profits or other special, indirect and consequential damages, even if
49 * Sun has been advised of the possibility of such damages.
51 * Sun Microsystems, Inc.
53 * Mountain View, California 94043
56 #include <sys/cdefs.h>
57 #if defined(LIBC_SCCS) && !defined(lint)
58 static char *sccsid
= "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
59 static char *sccsid
= "@(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC";
61 #include <sys/cdefs.h>
64 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
65 * layer above tcp (for rpc's use).
67 * Copyright (C) 1984, Sun Microsystems, Inc.
69 * These routines interface XDRSTREAMS to a tcp/ip connection.
70 * There is a record marking layer between the xdr stream
71 * and the tcp transport level. A record is composed on one or more
72 * record fragments. A record fragment is a thirty-two bit header followed
73 * by n bytes of data, where n is contained in the header. The header
74 * is represented as a htonl(u_long). Thegh order bit encodes
75 * whether or not the fragment is the last fragment of the record
76 * (1 => fragment is last, 0 => more fragments to follow.
77 * The other 31 bits encode the byte length of the fragment.
80 #include <sys/types.h>
82 #include <netinet/in.h>
89 #include <rpc/types.h>
95 static bool_t
xdrrec_getlong(XDR
*, long *);
96 static bool_t
xdrrec_putlong(XDR
*, const long *);
97 static bool_t
xdrrec_getbytes(XDR
*, char *, u_int
);
99 static bool_t
xdrrec_putbytes(XDR
*, const char *, u_int
);
100 static u_int
xdrrec_getpos(XDR
*);
101 static bool_t
xdrrec_setpos(XDR
*, u_int
);
102 static int32_t *xdrrec_inline(XDR
*, u_int
);
103 static void xdrrec_destroy(XDR
*);
105 static const struct xdr_ops xdrrec_ops
= {
117 * A record is composed of one or more record fragments.
118 * A record fragment is a four-byte header followed by zero to
119 * 2**32-1 bytes. The header is treated as a long unsigned and is
120 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
121 * are a byte count of the fragment. The highest order bit is a boolean:
122 * 1 => this fragment is the last fragment of the record,
123 * 0 => this fragment is followed by more fragment(s).
125 * The fragment/record machinery is not general; it is constructed to
126 * meet the needs of xdr and rpc based on tcp.
129 #define LAST_FRAG ((u_int32_t)(1 << 31))
131 typedef struct rec_strm
{
136 int (*writeit
)(void *, void *, int);
137 char *out_base
; /* output buffer (points to frag header) */
138 char *out_finger
; /* next output position */
139 char *out_boundry
; /* data cannot up to this address */
140 u_int32_t
*frag_header
; /* beginning of curren fragment */
141 bool_t frag_sent
; /* true if buffer sent in middle of record */
145 int (*readit
)(void *, void *, int);
146 u_long in_size
; /* fixed size of the input buffer */
148 char *in_finger
; /* location of next byte to be had */
149 char *in_boundry
; /* can read up to this location */
150 long fbtbc
; /* fragment bytes to be consumed */
156 bool_t in_haveheader
;
165 static u_int
fix_buf_size(u_int
);
166 static bool_t
flush_out(RECSTREAM
*, bool_t
);
167 static bool_t
fill_input_buf(RECSTREAM
*);
168 static bool_t
get_input_bytes(RECSTREAM
*, char *, int);
169 static bool_t
set_input_fragment(RECSTREAM
*);
170 static bool_t
skip_input_bytes(RECSTREAM
*, long);
171 static bool_t
realloc_stream(RECSTREAM
*, int);
175 * Create an xdr handle for xdrrec
176 * xdrrec_create fills in xdrs. Sendsize and recvsize are
177 * send and recv buffer sizes (0 => use default).
178 * tcp_handle is an opaque handle that is passed as the first parameter to
179 * the procedures readit and writeit. Readit and writeit are read and
180 * write respectively. They are like the system
181 * calls expect that they take an opaque handle rather than an fd.
184 xdrrec_create(xdrs
, sendsize
, recvsize
, tcp_handle
, readit
, writeit
)
189 /* like read, but pass it a tcp_handle, not sock */
190 int (*readit
)(void *, void *, int);
191 /* like write, but pass it a tcp_handle, not sock */
192 int (*writeit
)(void *, void *, int);
194 RECSTREAM
*rstrm
= mem_alloc(sizeof(RECSTREAM
));
197 warnx("xdrrec_create: out of memory");
199 * This is bad. Should rework xdrrec_create to
200 * return a handle, and in this case return NULL
204 rstrm
->sendsize
= sendsize
= fix_buf_size(sendsize
);
205 rstrm
->out_base
= mem_alloc(rstrm
->sendsize
);
206 if (rstrm
->out_base
== NULL
) {
207 warnx("xdrrec_create: out of memory");
208 mem_free(rstrm
, sizeof(RECSTREAM
));
211 rstrm
->recvsize
= recvsize
= fix_buf_size(recvsize
);
212 rstrm
->in_base
= mem_alloc(recvsize
);
213 if (rstrm
->in_base
== NULL
) {
214 warnx("xdrrec_create: out of memory");
215 mem_free(rstrm
->out_base
, sendsize
);
216 mem_free(rstrm
, sizeof(RECSTREAM
));
222 xdrs
->x_ops
= &xdrrec_ops
;
223 xdrs
->x_private
= rstrm
;
224 rstrm
->tcp_handle
= tcp_handle
;
225 rstrm
->readit
= readit
;
226 rstrm
->writeit
= writeit
;
227 rstrm
->out_finger
= rstrm
->out_boundry
= rstrm
->out_base
;
228 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
229 rstrm
->out_finger
+= sizeof(u_int32_t
);
230 rstrm
->out_boundry
+= sendsize
;
231 rstrm
->frag_sent
= FALSE
;
232 rstrm
->in_size
= recvsize
;
233 rstrm
->in_boundry
= rstrm
->in_base
;
234 rstrm
->in_finger
= (rstrm
->in_boundry
+= recvsize
);
236 rstrm
->last_frag
= TRUE
;
237 rstrm
->in_haveheader
= FALSE
;
238 rstrm
->in_hdrlen
= 0;
239 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
240 rstrm
->nonblock
= FALSE
;
241 rstrm
->in_reclen
= 0;
242 rstrm
->in_received
= 0;
247 * The reoutines defined below are the xdr ops which will go into the
248 * xdr handle filled in by xdrrec_create.
252 xdrrec_getlong(xdrs
, lp
)
256 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
257 int32_t *buflp
= (int32_t *)(void *)(rstrm
->in_finger
);
260 /* first try the inline, fast case */
261 if ((rstrm
->fbtbc
>= sizeof(int32_t)) &&
262 (((long)rstrm
->in_boundry
- (long)buflp
) >= sizeof(int32_t))) {
263 *lp
= (long)ntohl((u_int32_t
)(*buflp
));
264 rstrm
->fbtbc
-= sizeof(int32_t);
265 rstrm
->in_finger
+= sizeof(int32_t);
267 if (! xdrrec_getbytes(xdrs
, (char *)(void *)&mylong
,
270 *lp
= (long)ntohl((u_int32_t
)mylong
);
276 xdrrec_putlong(xdrs
, lp
)
280 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
281 int32_t *dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
283 if ((rstrm
->out_finger
+= sizeof(int32_t)) > rstrm
->out_boundry
) {
285 * this case should almost never happen so the code is
288 rstrm
->out_finger
-= sizeof(int32_t);
289 rstrm
->frag_sent
= TRUE
;
290 if (! flush_out(rstrm
, FALSE
))
292 dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
293 rstrm
->out_finger
+= sizeof(int32_t);
295 *dest_lp
= (int32_t)htonl((u_int32_t
)(*lp
));
299 static bool_t
/* must manage buffers, fragments, and records */
300 xdrrec_getbytes(xdrs
, addr
, len
)
305 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
309 current
= (int)rstrm
->fbtbc
;
311 if (rstrm
->last_frag
)
313 if (! set_input_fragment(rstrm
))
317 current
= (len
< current
) ? len
: current
;
318 if (! get_input_bytes(rstrm
, addr
, current
))
321 rstrm
->fbtbc
-= current
;
328 xdrrec_putbytes(xdrs
, addr
, len
)
333 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
337 current
= (size_t)((u_long
)rstrm
->out_boundry
-
338 (u_long
)rstrm
->out_finger
);
339 current
= (len
< current
) ? len
: current
;
340 memmove(rstrm
->out_finger
, addr
, current
);
341 rstrm
->out_finger
+= current
;
344 if (rstrm
->out_finger
== rstrm
->out_boundry
) {
345 rstrm
->frag_sent
= TRUE
;
346 if (! flush_out(rstrm
, FALSE
))
357 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
360 pos
= lseek((int)(u_long
)rstrm
->tcp_handle
, (off_t
)0, 1);
362 switch (xdrs
->x_op
) {
365 pos
+= rstrm
->out_finger
- rstrm
->out_base
;
369 pos
-= rstrm
->in_boundry
- rstrm
->in_finger
;
376 return ((u_int
) pos
);
380 xdrrec_setpos(xdrs
, pos
)
384 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
385 u_int currpos
= xdrrec_getpos(xdrs
);
386 int delta
= currpos
- pos
;
389 if ((int)currpos
!= -1)
390 switch (xdrs
->x_op
) {
393 newpos
= rstrm
->out_finger
- delta
;
394 if ((newpos
> (char *)(void *)(rstrm
->frag_header
)) &&
395 (newpos
< rstrm
->out_boundry
)) {
396 rstrm
->out_finger
= newpos
;
402 newpos
= rstrm
->in_finger
- delta
;
403 if ((delta
< (int)(rstrm
->fbtbc
)) &&
404 (newpos
<= rstrm
->in_boundry
) &&
405 (newpos
>= rstrm
->in_base
)) {
406 rstrm
->in_finger
= newpos
;
407 rstrm
->fbtbc
-= delta
;
419 xdrrec_inline(xdrs
, len
)
423 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
426 switch (xdrs
->x_op
) {
429 if ((rstrm
->out_finger
+ len
) <= rstrm
->out_boundry
) {
430 buf
= (int32_t *)(void *)rstrm
->out_finger
;
431 rstrm
->out_finger
+= len
;
436 if ((len
<= rstrm
->fbtbc
) &&
437 ((rstrm
->in_finger
+ len
) <= rstrm
->in_boundry
)) {
438 buf
= (int32_t *)(void *)rstrm
->in_finger
;
440 rstrm
->in_finger
+= len
;
454 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
456 mem_free(rstrm
->out_base
, rstrm
->sendsize
);
457 mem_free(rstrm
->in_base
, rstrm
->recvsize
);
458 mem_free(rstrm
, sizeof(RECSTREAM
));
463 * Exported routines to manage xdr records
467 * Before reading (deserializing from the stream, one should always call
468 * this procedure to guarantee proper record alignment.
471 xdrrec_skiprecord(xdrs
)
474 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
475 enum xprt_stat xstat
;
477 if (rstrm
->nonblock
) {
478 if (__xdrrec_getrec(xdrs
, &xstat
, FALSE
)) {
482 if (rstrm
->in_finger
== rstrm
->in_boundry
&&
483 xstat
== XPRT_MOREREQS
) {
490 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
491 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
494 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
497 rstrm
->last_frag
= FALSE
;
502 * Look ahead function.
503 * Returns TRUE iff there is no more input in the buffer
504 * after consuming the rest of the current record.
510 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
512 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
513 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
516 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
519 if (rstrm
->in_finger
== rstrm
->in_boundry
)
525 * The client must tell the package when an end-of-record has occurred.
526 * The second paraemters tells whether the record should be flushed to the
527 * (output) tcp stream. (This let's the package support batched or
528 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
531 xdrrec_endofrecord(xdrs
, sendnow
)
535 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
536 u_long len
; /* fragment length */
538 if (sendnow
|| rstrm
->frag_sent
||
539 ((u_long
)rstrm
->out_finger
+ sizeof(u_int32_t
) >=
540 (u_long
)rstrm
->out_boundry
)) {
541 rstrm
->frag_sent
= FALSE
;
542 return (flush_out(rstrm
, TRUE
));
544 len
= (u_long
)(rstrm
->out_finger
) - (u_long
)(rstrm
->frag_header
) -
546 *(rstrm
->frag_header
) = htonl((u_int32_t
)len
| LAST_FRAG
);
547 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_finger
;
548 rstrm
->out_finger
+= sizeof(u_int32_t
);
553 * Fill the stream buffer with a record for a non-blocking connection.
554 * Return true if a record is available in the buffer, false if not.
557 __xdrrec_getrec(xdrs
, statp
, expectdata
)
559 enum xprt_stat
*statp
;
562 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
566 if (!rstrm
->in_haveheader
) {
567 n
= rstrm
->readit(rstrm
->tcp_handle
, rstrm
->in_hdrp
,
568 (int)sizeof (rstrm
->in_header
) - rstrm
->in_hdrlen
);
570 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
578 rstrm
->in_hdrlen
+= n
;
579 if (rstrm
->in_hdrlen
< sizeof (rstrm
->in_header
)) {
580 *statp
= XPRT_MOREREQS
;
583 rstrm
->in_header
= ntohl(rstrm
->in_header
);
584 fraglen
= (int)(rstrm
->in_header
& ~LAST_FRAG
);
585 if (fraglen
== 0 || fraglen
> rstrm
->in_maxrec
||
586 (rstrm
->in_reclen
+ fraglen
) > rstrm
->in_maxrec
) {
590 rstrm
->in_reclen
+= fraglen
;
591 if (rstrm
->in_reclen
> rstrm
->recvsize
)
592 realloc_stream(rstrm
, rstrm
->in_reclen
);
593 if (rstrm
->in_header
& LAST_FRAG
) {
594 rstrm
->in_header
&= ~LAST_FRAG
;
595 rstrm
->last_frag
= TRUE
;
599 n
= rstrm
->readit(rstrm
->tcp_handle
,
600 rstrm
->in_base
+ rstrm
->in_received
,
601 (rstrm
->in_reclen
- rstrm
->in_received
));
609 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
613 rstrm
->in_received
+= n
;
615 if (rstrm
->in_received
== rstrm
->in_reclen
) {
616 rstrm
->in_haveheader
= FALSE
;
617 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
618 rstrm
->in_hdrlen
= 0;
619 if (rstrm
->last_frag
) {
620 rstrm
->fbtbc
= rstrm
->in_reclen
;
621 rstrm
->in_boundry
= rstrm
->in_base
+ rstrm
->in_reclen
;
622 rstrm
->in_finger
= rstrm
->in_base
;
623 rstrm
->in_reclen
= rstrm
->in_received
= 0;
624 *statp
= XPRT_MOREREQS
;
629 *statp
= XPRT_MOREREQS
;
634 __xdrrec_setnonblock(xdrs
, maxrec
)
638 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
640 rstrm
->nonblock
= TRUE
;
642 maxrec
= rstrm
->recvsize
;
643 rstrm
->in_maxrec
= maxrec
;
648 * Internal useful routines
651 flush_out(rstrm
, eor
)
655 u_int32_t eormask
= (eor
== TRUE
) ? LAST_FRAG
: 0;
656 u_int32_t len
= (u_int32_t
)((u_long
)(rstrm
->out_finger
) -
657 (u_long
)(rstrm
->frag_header
) - sizeof(u_int32_t
));
659 *(rstrm
->frag_header
) = htonl(len
| eormask
);
660 len
= (u_int32_t
)((u_long
)(rstrm
->out_finger
) -
661 (u_long
)(rstrm
->out_base
));
662 if ((*(rstrm
->writeit
))(rstrm
->tcp_handle
, rstrm
->out_base
, (int)len
)
665 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
666 rstrm
->out_finger
= (char *)rstrm
->out_base
+ sizeof(u_int32_t
);
670 static bool_t
/* knows nothing about records! Only about input buffers */
671 fill_input_buf(rstrm
)
681 where
= rstrm
->in_base
;
682 i
= (u_int32_t
)((u_long
)rstrm
->in_boundry
% BYTES_PER_XDR_UNIT
);
684 len
= (u_int32_t
)(rstrm
->in_size
- i
);
685 if ((len
= (*(rstrm
->readit
))(rstrm
->tcp_handle
, where
, len
)) == -1)
687 rstrm
->in_finger
= where
;
689 rstrm
->in_boundry
= where
;
693 static bool_t
/* knows nothing about records! Only about input buffers */
694 get_input_bytes(rstrm
, addr
, len
)
701 if (rstrm
->nonblock
) {
702 if (len
> (int)(rstrm
->in_boundry
- rstrm
->in_finger
))
704 memcpy(addr
, rstrm
->in_finger
, (size_t)len
);
705 rstrm
->in_finger
+= len
;
710 current
= (size_t)((long)rstrm
->in_boundry
-
711 (long)rstrm
->in_finger
);
713 if (! fill_input_buf(rstrm
))
717 current
= (len
< current
) ? len
: current
;
718 memmove(addr
, rstrm
->in_finger
, current
);
719 rstrm
->in_finger
+= current
;
726 static bool_t
/* next two bytes of the input stream are treated as a header */
727 set_input_fragment(rstrm
)
734 if (! get_input_bytes(rstrm
, (char *)(void *)&header
, sizeof(header
)))
736 header
= ntohl(header
);
737 rstrm
->last_frag
= ((header
& LAST_FRAG
) == 0) ? FALSE
: TRUE
;
739 * Sanity check. Try not to accept wildly incorrect
740 * record sizes. Unfortunately, the only record size
741 * we can positively identify as being 'wildly incorrect'
742 * is zero. Ridiculously large record sizes may look wrong,
743 * but we don't have any way to be certain that they aren't
744 * what the client actually intended to send us.
748 rstrm
->fbtbc
= header
& (~LAST_FRAG
);
752 static bool_t
/* consumes input bytes; knows nothing about records! */
753 skip_input_bytes(rstrm
, cnt
)
760 current
= (size_t)((long)rstrm
->in_boundry
-
761 (long)rstrm
->in_finger
);
763 if (! fill_input_buf(rstrm
))
767 current
= (u_int32_t
)((cnt
< current
) ? cnt
: current
);
768 rstrm
->in_finger
+= current
;
785 * Reallocate the input buffer for a non-block stream.
788 realloc_stream(rstrm
, size
)
795 if (size
> rstrm
->recvsize
) {
796 buf
= realloc(rstrm
->in_base
, (size_t)size
);
799 diff
= buf
- rstrm
->in_base
;
800 rstrm
->in_finger
+= diff
;
801 rstrm
->in_base
= buf
;
802 rstrm
->in_boundry
= buf
+ size
;
803 rstrm
->recvsize
= size
;
804 rstrm
->in_size
= size
;