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.
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.
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.
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.
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.
25 * Sun Microsystems, Inc.
27 * Mountain View, California 94043
29 * @(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro
30 * @(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC
31 * $NetBSD: xdr_rec.c,v 1.18 2000/07/06 03:10:35 christos Exp $
32 * $FreeBSD: src/lib/libc/xdr/xdr_rec.c,v 1.22 2008/03/30 09:35:04 dfr Exp $
33 * $DragonFly: src/lib/libc/xdr/xdr_rec.c,v 1.5 2005/12/05 00:47:57 swildner Exp $
37 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
38 * layer above tcp (for rpc's use).
40 * Copyright (C) 1984, Sun Microsystems, Inc.
42 * These routines interface XDRSTREAMS to a tcp/ip connection.
43 * There is a record marking layer between the xdr stream
44 * and the tcp transport level. A record is composed on one or more
45 * record fragments. A record fragment is a thirty-two bit header followed
46 * by n bytes of data, where n is contained in the header. The header
47 * is represented as a htonl(u_long). Thegh order bit encodes
48 * whether or not the fragment is the last fragment of the record
49 * (1 => fragment is last, 0 => more fragments to follow.
50 * The other 31 bits encode the byte length of the fragment.
53 #include "namespace.h"
54 #include <sys/types.h>
56 #include <netinet/in.h>
64 #include <rpc/types.h>
69 #include "un-namespace.h"
72 static bool_t
xdrrec_getlong(XDR
*, long *);
73 static bool_t
xdrrec_putlong(XDR
*, const long *);
74 static bool_t
xdrrec_getbytes(XDR
*, char *, u_int
);
76 static bool_t
xdrrec_putbytes(XDR
*, const char *, u_int
);
77 static u_int
xdrrec_getpos(XDR
*);
78 static bool_t
xdrrec_setpos(XDR
*, u_int
);
79 static int32_t *xdrrec_inline(XDR
*, u_int
);
80 static void xdrrec_destroy(XDR
*);
82 static const struct xdr_ops xdrrec_ops
= {
94 * A record is composed of one or more record fragments.
95 * A record fragment is a four-byte header followed by zero to
96 * 2**32-1 bytes. The header is treated as a long unsigned and is
97 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
98 * are a byte count of the fragment. The highest order bit is a boolean:
99 * 1 => this fragment is the last fragment of the record,
100 * 0 => this fragment is followed by more fragment(s).
102 * The fragment/record machinery is not general; it is constructed to
103 * meet the needs of xdr and rpc based on tcp.
106 #define LAST_FRAG ((u_int32_t)(1 << 31))
108 typedef struct rec_strm
{
113 int (*writeit
)(void *, void *, int);
114 char *out_base
; /* output buffer (points to frag header) */
115 char *out_finger
; /* next output position */
116 char *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 */
122 int (*readit
)(void *, void *, int);
123 u_long in_size
; /* fixed size of the input buffer */
125 char *in_finger
; /* location of next byte to be had */
126 char *in_boundry
; /* can read up to this location */
127 long fbtbc
; /* fragment bytes to be consumed */
133 bool_t in_haveheader
;
142 static u_int
fix_buf_size(u_int
);
143 static bool_t
flush_out(RECSTREAM
*, bool_t
);
144 static bool_t
fill_input_buf(RECSTREAM
*);
145 static bool_t
get_input_bytes(RECSTREAM
*, char *, int);
146 static bool_t
set_input_fragment(RECSTREAM
*);
147 static bool_t
skip_input_bytes(RECSTREAM
*, long);
148 static bool_t
realloc_stream(RECSTREAM
*, int);
151 * Create an xdr handle for xdrrec
152 * xdrrec_create fills in xdrs. Sendsize and recvsize are
153 * send and recv buffer sizes (0 => use default).
154 * tcp_handle is an opaque handle that is passed as the first parameter to
155 * the procedures readit and writeit. Readit and writeit are read and
156 * write respectively. They are like the system
157 * calls expect that they take an opaque handle rather than an fd.
160 * readit: like read, but pass it a tcp_handle, not sock
161 * writeit: lite write, but pass it a tcp_handle, not sock
164 xdrrec_create(XDR
*xdrs
, u_int sendsize
, u_int recvsize
, void *tcp_handle
,
165 int (*readit
)(void *, void *, int),
166 int (*writeit
)(void *, void *, int))
168 RECSTREAM
*rstrm
= mem_alloc(sizeof(RECSTREAM
));
171 warnx("xdrrec_create: out of memory");
173 * This is bad. Should rework xdrrec_create to
174 * return a handle, and in this case return NULL
178 rstrm
->sendsize
= sendsize
= fix_buf_size(sendsize
);
179 rstrm
->out_base
= mem_alloc(rstrm
->sendsize
);
180 if (rstrm
->out_base
== NULL
) {
181 warnx("xdrrec_create: out of memory");
182 mem_free(rstrm
, sizeof(RECSTREAM
));
185 rstrm
->recvsize
= recvsize
= fix_buf_size(recvsize
);
186 rstrm
->in_base
= mem_alloc(recvsize
);
187 if (rstrm
->in_base
== NULL
) {
188 warnx("xdrrec_create: out of memory");
189 mem_free(rstrm
->out_base
, sendsize
);
190 mem_free(rstrm
, sizeof(RECSTREAM
));
196 xdrs
->x_ops
= &xdrrec_ops
;
197 xdrs
->x_private
= rstrm
;
198 rstrm
->tcp_handle
= tcp_handle
;
199 rstrm
->readit
= readit
;
200 rstrm
->writeit
= writeit
;
201 rstrm
->out_finger
= rstrm
->out_boundry
= rstrm
->out_base
;
202 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
203 rstrm
->out_finger
+= sizeof(u_int32_t
);
204 rstrm
->out_boundry
+= sendsize
;
205 rstrm
->frag_sent
= FALSE
;
206 rstrm
->in_size
= recvsize
;
207 rstrm
->in_boundry
= rstrm
->in_base
;
208 rstrm
->in_finger
= (rstrm
->in_boundry
+= recvsize
);
210 rstrm
->last_frag
= TRUE
;
211 rstrm
->in_haveheader
= FALSE
;
212 rstrm
->in_hdrlen
= 0;
213 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
214 rstrm
->nonblock
= FALSE
;
215 rstrm
->in_reclen
= 0;
216 rstrm
->in_received
= 0;
221 * The routines defined below are the xdr ops which will go into the
222 * xdr handle filled in by xdrrec_create.
226 xdrrec_getlong(XDR
*xdrs
, long *lp
)
228 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
229 int32_t *buflp
= (int32_t *)(void *)(rstrm
->in_finger
);
232 /* first try the inline, fast case */
233 if ((rstrm
->fbtbc
>= sizeof(int32_t)) &&
234 (((long)rstrm
->in_boundry
- (long)buflp
) >= sizeof(int32_t))) {
235 *lp
= (long)ntohl((u_int32_t
)(*buflp
));
236 rstrm
->fbtbc
-= sizeof(int32_t);
237 rstrm
->in_finger
+= sizeof(int32_t);
239 if (! xdrrec_getbytes(xdrs
, (char *)(void *)&mylong
,
242 *lp
= (long)ntohl((u_int32_t
)mylong
);
248 xdrrec_putlong(XDR
*xdrs
, const long *lp
)
250 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
251 int32_t *dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
253 if ((rstrm
->out_finger
+= sizeof(int32_t)) > rstrm
->out_boundry
) {
255 * this case should almost never happen so the code is
258 rstrm
->out_finger
-= sizeof(int32_t);
259 rstrm
->frag_sent
= TRUE
;
260 if (! flush_out(rstrm
, FALSE
))
262 dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
263 rstrm
->out_finger
+= sizeof(int32_t);
265 *dest_lp
= (int32_t)htonl((u_int32_t
)(*lp
));
269 static bool_t
/* must manage buffers, fragments, and records */
270 xdrrec_getbytes(XDR
*xdrs
, char *addr
, u_int len
)
272 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
276 current
= (int)rstrm
->fbtbc
;
278 if (rstrm
->last_frag
)
280 if (! set_input_fragment(rstrm
))
284 current
= (len
< current
) ? len
: current
;
285 if (! get_input_bytes(rstrm
, addr
, current
))
288 rstrm
->fbtbc
-= current
;
295 xdrrec_putbytes(XDR
*xdrs
, const char *addr
, u_int len
)
297 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
301 current
= (size_t)((u_long
)rstrm
->out_boundry
-
302 (u_long
)rstrm
->out_finger
);
303 current
= (len
< current
) ? len
: current
;
304 memmove(rstrm
->out_finger
, addr
, current
);
305 rstrm
->out_finger
+= current
;
308 if (rstrm
->out_finger
== rstrm
->out_boundry
) {
309 rstrm
->frag_sent
= TRUE
;
310 if (! flush_out(rstrm
, FALSE
))
318 xdrrec_getpos(XDR
*xdrs
)
320 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
323 pos
= lseek((int)(u_long
)rstrm
->tcp_handle
, (off_t
)0, 1);
325 switch (xdrs
->x_op
) {
328 pos
+= rstrm
->out_finger
- rstrm
->out_base
;
332 pos
-= rstrm
->in_boundry
- rstrm
->in_finger
;
339 return ((u_int
) pos
);
343 xdrrec_setpos(XDR
*xdrs
, u_int pos
)
345 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
346 u_int currpos
= xdrrec_getpos(xdrs
);
347 int delta
= currpos
- pos
;
350 if ((int)currpos
!= -1)
351 switch (xdrs
->x_op
) {
354 newpos
= rstrm
->out_finger
- delta
;
355 if ((newpos
> (char *)(void *)(rstrm
->frag_header
)) &&
356 (newpos
< rstrm
->out_boundry
)) {
357 rstrm
->out_finger
= newpos
;
363 newpos
= rstrm
->in_finger
- delta
;
364 if ((delta
< (int)(rstrm
->fbtbc
)) &&
365 (newpos
<= rstrm
->in_boundry
) &&
366 (newpos
>= rstrm
->in_base
)) {
367 rstrm
->in_finger
= newpos
;
368 rstrm
->fbtbc
-= delta
;
380 xdrrec_inline(XDR
*xdrs
, u_int len
)
382 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
385 switch (xdrs
->x_op
) {
388 if ((rstrm
->out_finger
+ len
) <= rstrm
->out_boundry
) {
389 buf
= (int32_t *)(void *)rstrm
->out_finger
;
390 rstrm
->out_finger
+= len
;
395 if ((len
<= rstrm
->fbtbc
) &&
396 ((rstrm
->in_finger
+ len
) <= rstrm
->in_boundry
)) {
397 buf
= (int32_t *)(void *)rstrm
->in_finger
;
399 rstrm
->in_finger
+= len
;
409 xdrrec_destroy(XDR
*xdrs
)
411 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
413 mem_free(rstrm
->out_base
, rstrm
->sendsize
);
414 mem_free(rstrm
->in_base
, rstrm
->recvsize
);
415 mem_free(rstrm
, sizeof(RECSTREAM
));
420 * Exported routines to manage xdr records
424 * Before reading (deserializing from the stream, one should always call
425 * this procedure to guarantee proper record alignment.
428 xdrrec_skiprecord(XDR
*xdrs
)
430 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
431 enum xprt_stat xstat
;
433 if (rstrm
->nonblock
) {
434 if (__xdrrec_getrec(xdrs
, &xstat
, FALSE
)) {
438 if (rstrm
->in_finger
== rstrm
->in_boundry
&&
439 xstat
== XPRT_MOREREQS
) {
446 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
447 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
450 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
453 rstrm
->last_frag
= FALSE
;
458 * Look ahead function.
459 * Returns TRUE iff there is no more input in the buffer
460 * after consuming the rest of the current record.
463 xdrrec_eof(XDR
*xdrs
)
465 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
467 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
468 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
471 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
474 if (rstrm
->in_finger
== rstrm
->in_boundry
)
480 * The client must tell the package when an end-of-record has occurred.
481 * The second paraemters tells whether the record should be flushed to the
482 * (output) tcp stream. (This let's the package support batched or
483 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
486 xdrrec_endofrecord(XDR
*xdrs
, bool_t sendnow
)
488 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
489 u_long len
; /* fragment length */
491 if (sendnow
|| rstrm
->frag_sent
||
492 ((u_long
)rstrm
->out_finger
+ sizeof(u_int32_t
) >=
493 (u_long
)rstrm
->out_boundry
)) {
494 rstrm
->frag_sent
= FALSE
;
495 return (flush_out(rstrm
, TRUE
));
497 len
= (u_long
)(rstrm
->out_finger
) - (u_long
)(rstrm
->frag_header
) -
499 *(rstrm
->frag_header
) = htonl((u_int32_t
)len
| LAST_FRAG
);
500 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_finger
;
501 rstrm
->out_finger
+= sizeof(u_int32_t
);
506 * Fill the stream buffer with a record for a non-blocking connection.
507 * Return true if a record is available in the buffer, false if not.
510 __xdrrec_getrec(XDR
*xdrs
, enum xprt_stat
*statp
, bool_t expectdata
)
512 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
516 if (!rstrm
->in_haveheader
) {
517 n
= rstrm
->readit(rstrm
->tcp_handle
, rstrm
->in_hdrp
,
518 (int)sizeof (rstrm
->in_header
) - rstrm
->in_hdrlen
);
520 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
528 rstrm
->in_hdrlen
+= n
;
529 if (rstrm
->in_hdrlen
< sizeof (rstrm
->in_header
)) {
530 *statp
= XPRT_MOREREQS
;
533 rstrm
->in_header
= ntohl(rstrm
->in_header
);
534 fraglen
= (int)(rstrm
->in_header
& ~LAST_FRAG
);
535 if (fraglen
== 0 || fraglen
> rstrm
->in_maxrec
||
536 (rstrm
->in_reclen
+ fraglen
) > rstrm
->in_maxrec
) {
540 rstrm
->in_reclen
+= fraglen
;
541 if (rstrm
->in_reclen
> rstrm
->recvsize
)
542 realloc_stream(rstrm
, rstrm
->in_reclen
);
543 if (rstrm
->in_header
& LAST_FRAG
) {
544 rstrm
->in_header
&= ~LAST_FRAG
;
545 rstrm
->last_frag
= TRUE
;
548 * We can only reasonably expect to read once from a
549 * non-blocking stream. Reading the fragment header
550 * may have drained the stream.
555 n
= rstrm
->readit(rstrm
->tcp_handle
,
556 rstrm
->in_base
+ rstrm
->in_received
,
557 (rstrm
->in_reclen
- rstrm
->in_received
));
565 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
569 rstrm
->in_received
+= n
;
571 if (rstrm
->in_received
== rstrm
->in_reclen
) {
572 rstrm
->in_haveheader
= FALSE
;
573 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
574 rstrm
->in_hdrlen
= 0;
575 if (rstrm
->last_frag
) {
576 rstrm
->fbtbc
= rstrm
->in_reclen
;
577 rstrm
->in_boundry
= rstrm
->in_base
+ rstrm
->in_reclen
;
578 rstrm
->in_finger
= rstrm
->in_base
;
579 rstrm
->in_reclen
= rstrm
->in_received
= 0;
580 *statp
= XPRT_MOREREQS
;
585 *statp
= XPRT_MOREREQS
;
590 __xdrrec_setnonblock(XDR
*xdrs
, int maxrec
)
592 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
594 rstrm
->nonblock
= TRUE
;
596 maxrec
= rstrm
->recvsize
;
597 rstrm
->in_maxrec
= maxrec
;
602 * Internal useful routines
605 flush_out(RECSTREAM
*rstrm
, bool_t eor
)
607 u_int32_t eormask
= (eor
== TRUE
) ? LAST_FRAG
: 0;
608 u_int32_t len
= (u_int32_t
)((u_long
)(rstrm
->out_finger
) -
609 (u_long
)(rstrm
->frag_header
) - sizeof(u_int32_t
));
611 *(rstrm
->frag_header
) = htonl(len
| eormask
);
612 len
= (u_int32_t
)((u_long
)(rstrm
->out_finger
) -
613 (u_long
)(rstrm
->out_base
));
614 if ((*(rstrm
->writeit
))(rstrm
->tcp_handle
, rstrm
->out_base
, (int)len
)
617 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
618 rstrm
->out_finger
= (char *)rstrm
->out_base
+ sizeof(u_int32_t
);
622 static bool_t
/* knows nothing about records! Only about input buffers */
623 fill_input_buf(RECSTREAM
*rstrm
)
632 where
= rstrm
->in_base
;
633 i
= (u_int32_t
)((u_long
)rstrm
->in_boundry
% BYTES_PER_XDR_UNIT
);
635 len
= (u_int32_t
)(rstrm
->in_size
- i
);
636 if ((len
= (*(rstrm
->readit
))(rstrm
->tcp_handle
, where
, len
)) == -1)
638 rstrm
->in_finger
= where
;
640 rstrm
->in_boundry
= where
;
644 static bool_t
/* knows nothing about records! Only about input buffers */
645 get_input_bytes(RECSTREAM
*rstrm
, char *addr
, int len
)
649 if (rstrm
->nonblock
) {
650 if (len
> (int)(rstrm
->in_boundry
- rstrm
->in_finger
))
652 memcpy(addr
, rstrm
->in_finger
, (size_t)len
);
653 rstrm
->in_finger
+= len
;
658 current
= (size_t)((long)rstrm
->in_boundry
-
659 (long)rstrm
->in_finger
);
661 if (! fill_input_buf(rstrm
))
665 current
= (len
< current
) ? len
: current
;
666 memmove(addr
, rstrm
->in_finger
, current
);
667 rstrm
->in_finger
+= current
;
674 static bool_t
/* next two bytes of the input stream are treated as a header */
675 set_input_fragment(RECSTREAM
*rstrm
)
681 if (! get_input_bytes(rstrm
, (char *)(void *)&header
, sizeof(header
)))
683 header
= ntohl(header
);
684 rstrm
->last_frag
= ((header
& LAST_FRAG
) == 0) ? FALSE
: TRUE
;
686 * Sanity check. Try not to accept wildly incorrect
687 * record sizes. Unfortunately, the only record size
688 * we can positively identify as being 'wildly incorrect'
689 * is zero. Ridiculously large record sizes may look wrong,
690 * but we don't have any way to be certain that they aren't
691 * what the client actually intended to send us.
695 rstrm
->fbtbc
= header
& (~LAST_FRAG
);
699 static bool_t
/* consumes input bytes; knows nothing about records! */
700 skip_input_bytes(RECSTREAM
*rstrm
, long cnt
)
705 current
= (size_t)((long)rstrm
->in_boundry
-
706 (long)rstrm
->in_finger
);
708 if (! fill_input_buf(rstrm
))
712 current
= (u_int32_t
)((cnt
< current
) ? cnt
: current
);
713 rstrm
->in_finger
+= current
;
720 fix_buf_size(u_int s
)
729 * Reallocate the input buffer for a non-block stream.
732 realloc_stream(RECSTREAM
*rstrm
, int size
)
737 if (size
> rstrm
->recvsize
) {
738 buf
= realloc(rstrm
->in_base
, (size_t)size
);
741 diff
= buf
- rstrm
->in_base
;
742 rstrm
->in_finger
+= diff
;
743 rstrm
->in_base
= buf
;
744 rstrm
->in_boundry
= buf
+ size
;
745 rstrm
->recvsize
= size
;
746 rstrm
->in_size
= size
;