1 /* $NetBSD: xdr_rec.c,v 1.31 2010/11/23 14:02:01 christos Exp $ */
4 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5 * unrestricted use provided that this legend is included on all tape
6 * media and as a part of the software program in whole or part. Users
7 * may copy or modify Sun RPC without charge, but are not authorized
8 * to license or distribute it to anyone else except as part of a product or
9 * program developed by the user.
11 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
15 * Sun RPC is provided with no support and without any obligation on the
16 * part of Sun Microsystems, Inc. to assist in its use, correction,
17 * modification or enhancement.
19 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21 * OR ANY PART THEREOF.
23 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24 * or profits or other special, indirect and consequential damages, even if
25 * Sun has been advised of the possibility of such damages.
27 * Sun Microsystems, Inc.
29 * Mountain View, California 94043
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
35 static char *sccsid
= "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
36 static char *sccsid
= "@(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC";
38 __RCSID("$NetBSD: xdr_rec.c,v 1.31 2010/11/23 14:02:01 christos Exp $");
43 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
44 * layer above tcp (for rpc's use).
46 * Copyright (C) 1984, Sun Microsystems, Inc.
48 * These routines interface XDRSTREAMS to a tcp/ip connection.
49 * There is a record marking layer between the xdr stream
50 * and the tcp transport level. A record is composed on one or more
51 * record fragments. A record fragment is a thirty-two bit header followed
52 * by n bytes of data, where n is contained in the header. The header
53 * is represented as a htonl(u_long). Thegh order bit encodes
54 * whether or not the fragment is the last fragment of the record
55 * (1 => fragment is last, 0 => more fragments to follow.
56 * The other 31 bits encode the byte length of the fragment.
59 #include "namespace.h"
61 #include <sys/types.h>
63 #include <netinet/in.h>
71 #include <rpc/types.h>
77 #include "rpc_internal.h"
80 __weak_alias(xdrrec_create
,_xdrrec_create
)
81 __weak_alias(xdrrec_endofrecord
,_xdrrec_endofrecord
)
82 __weak_alias(xdrrec_eof
,_xdrrec_eof
)
83 __weak_alias(xdrrec_skiprecord
,_xdrrec_skiprecord
)
86 static bool_t xdrrec_getlong
__P((XDR
*, long *));
87 static bool_t xdrrec_putlong
__P((XDR
*, const long *));
88 static bool_t xdrrec_getbytes
__P((XDR
*, char *, u_int
));
90 static bool_t xdrrec_putbytes
__P((XDR
*, const char *, u_int
));
91 static u_int xdrrec_getpos
__P((XDR
*));
92 static bool_t xdrrec_setpos
__P((XDR
*, u_int
));
93 static int32_t *xdrrec_inline
__P((XDR
*, u_int
));
94 static void xdrrec_destroy
__P((XDR
*));
96 static const struct xdr_ops xdrrec_ops
= {
105 NULL
, /* xdrrec_control */
109 * A record is composed of one or more record fragments.
110 * A record fragment is a four-byte header followed by zero to
111 * 2**32-1 bytes. The header is treated as a long unsigned and is
112 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
113 * are a byte count of the fragment. The highest order bit is a boolean:
114 * 1 => this fragment is the last fragment of the record,
115 * 0 => this fragment is followed by more fragment(s).
117 * The fragment/record machinery is not general; it is constructed to
118 * meet the needs of xdr and rpc based on tcp.
121 #define LAST_FRAG ((u_int32_t)(1 << 31))
123 typedef struct rec_strm
{
128 int (*writeit
) __P((char *, char *, int));
129 char *out_base
; /* output buffer (points to frag header) */
130 char *out_finger
; /* next output position */
131 char *out_boundry
; /* data cannot up to this address */
132 u_int32_t
*frag_header
; /* beginning of curren fragment */
133 bool_t frag_sent
; /* true if buffer sent in middle of record */
137 int (*readit
) __P((char *, char *, int));
138 u_long in_size
; /* fixed size of the input buffer */
140 char *in_finger
; /* location of next byte to be had */
141 char *in_boundry
; /* can read up to this location */
142 long fbtbc
; /* fragment bytes to be consumed */
148 bool_t in_haveheader
;
157 static u_int fix_buf_size
__P((u_int
));
158 static bool_t flush_out
__P((RECSTREAM
*, bool_t
));
159 static bool_t fill_input_buf
__P((RECSTREAM
*));
160 static bool_t get_input_bytes
__P((RECSTREAM
*, char *, u_int
));
161 static bool_t set_input_fragment
__P((RECSTREAM
*));
162 static bool_t skip_input_bytes
__P((RECSTREAM
*, long));
163 static bool_t realloc_stream
__P((RECSTREAM
*, int));
167 * Create an xdr handle for xdrrec
168 * xdrrec_create fills in xdrs. Sendsize and recvsize are
169 * send and recv buffer sizes (0 => use default).
170 * tcp_handle is an opaque handle that is passed as the first parameter to
171 * the procedures readit and writeit. Readit and writeit are read and
172 * write respectively. They are like the system
173 * calls expect that they take an opaque handle rather than an fd.
176 xdrrec_create(xdrs
, sendsize
, recvsize
, tcp_handle
, readit
, writeit
)
181 /* like read, but pass it a tcp_handle, not sock */
182 int (*readit
) __P((char *, char *, int));
183 /* like write, but pass it a tcp_handle, not sock */
184 int (*writeit
) __P((char *, char *, int));
186 RECSTREAM
*rstrm
= mem_alloc(sizeof(RECSTREAM
));
189 warnx("xdrrec_create: out of memory");
191 * This is bad. Should rework xdrrec_create to
192 * return a handle, and in this case return NULL
197 rstrm
->sendsize
= sendsize
= fix_buf_size(sendsize
);
198 rstrm
->out_base
= malloc(rstrm
->sendsize
);
199 if (rstrm
->out_base
== NULL
) {
200 warnx("xdrrec_create: out of memory");
201 mem_free(rstrm
, sizeof(RECSTREAM
));
205 rstrm
->recvsize
= recvsize
= fix_buf_size(recvsize
);
206 rstrm
->in_base
= malloc(recvsize
);
207 if (rstrm
->in_base
== NULL
) {
208 warnx("xdrrec_create: out of memory");
209 mem_free(rstrm
->out_base
, sendsize
);
210 mem_free(rstrm
, sizeof(RECSTREAM
));
216 xdrs
->x_ops
= &xdrrec_ops
;
217 xdrs
->x_private
= rstrm
;
218 rstrm
->tcp_handle
= tcp_handle
;
219 rstrm
->readit
= readit
;
220 rstrm
->writeit
= writeit
;
221 rstrm
->out_finger
= rstrm
->out_boundry
= rstrm
->out_base
;
222 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
223 rstrm
->out_finger
+= sizeof(u_int32_t
);
224 rstrm
->out_boundry
+= sendsize
;
225 rstrm
->frag_sent
= FALSE
;
226 rstrm
->in_size
= recvsize
;
227 rstrm
->in_boundry
= rstrm
->in_base
;
228 rstrm
->in_finger
= (rstrm
->in_boundry
+= recvsize
);
230 rstrm
->last_frag
= TRUE
;
231 rstrm
->in_haveheader
= FALSE
;
232 rstrm
->in_hdrlen
= 0;
233 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
234 rstrm
->nonblock
= FALSE
;
235 rstrm
->in_reclen
= 0;
236 rstrm
->in_received
= 0;
241 * The reoutines defined below are the xdr ops which will go into the
242 * xdr handle filled in by xdrrec_create.
246 xdrrec_getlong(xdrs
, lp
)
250 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
251 int32_t *buflp
= (int32_t *)(void *)(rstrm
->in_finger
);
254 /* first try the inline, fast case */
255 if ((rstrm
->fbtbc
>= (long)sizeof(int32_t)) &&
256 (((uintptr_t)rstrm
->in_boundry
- (uintptr_t)buflp
) >= sizeof(int32_t))) {
257 *lp
= (long)ntohl((u_int32_t
)(*buflp
));
258 rstrm
->fbtbc
-= sizeof(int32_t);
259 rstrm
->in_finger
+= sizeof(int32_t);
261 if (! xdrrec_getbytes(xdrs
, (char *)(void *)&mylong
,
264 *lp
= (long)ntohl((u_int32_t
)mylong
);
270 xdrrec_putlong(xdrs
, lp
)
274 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
275 int32_t *dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
277 if ((rstrm
->out_finger
+= sizeof(int32_t)) > rstrm
->out_boundry
) {
279 * this case should almost never happen so the code is
282 rstrm
->out_finger
-= sizeof(int32_t);
283 rstrm
->frag_sent
= TRUE
;
284 if (! flush_out(rstrm
, FALSE
))
286 dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
287 rstrm
->out_finger
+= sizeof(int32_t);
289 *dest_lp
= (int32_t)htonl((u_int32_t
)(*lp
));
293 static bool_t
/* must manage buffers, fragments, and records */
294 xdrrec_getbytes(xdrs
, addr
, len
)
299 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
303 current
= (u_int
)rstrm
->fbtbc
;
305 if (rstrm
->last_frag
)
307 if (! set_input_fragment(rstrm
))
311 current
= (len
< current
) ? len
: current
;
312 if (! get_input_bytes(rstrm
, addr
, current
))
315 rstrm
->fbtbc
-= current
;
322 xdrrec_putbytes(xdrs
, addr
, len
)
327 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
331 current
= (size_t)((u_long
)rstrm
->out_boundry
-
332 (u_long
)rstrm
->out_finger
);
333 current
= (len
< current
) ? len
: current
;
334 memmove(rstrm
->out_finger
, addr
, current
);
335 rstrm
->out_finger
+= current
;
338 if (rstrm
->out_finger
== rstrm
->out_boundry
) {
339 rstrm
->frag_sent
= TRUE
;
340 if (! flush_out(rstrm
, FALSE
))
351 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
354 pos
= lseek((int)(u_long
)rstrm
->tcp_handle
, (off_t
)0, 1);
356 switch (xdrs
->x_op
) {
359 pos
+= rstrm
->out_finger
- rstrm
->out_base
;
363 pos
-= rstrm
->in_boundry
- rstrm
->in_finger
;
370 return ((u_int
) pos
);
374 xdrrec_setpos(xdrs
, pos
)
378 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
379 u_int currpos
= xdrrec_getpos(xdrs
);
380 int delta
= currpos
- pos
;
383 if ((int)currpos
!= -1)
384 switch (xdrs
->x_op
) {
387 newpos
= rstrm
->out_finger
- delta
;
388 if ((newpos
> (char *)(void *)(rstrm
->frag_header
)) &&
389 (newpos
< rstrm
->out_boundry
)) {
390 rstrm
->out_finger
= newpos
;
396 newpos
= rstrm
->in_finger
- delta
;
397 if ((delta
< (int)(rstrm
->fbtbc
)) &&
398 (newpos
<= rstrm
->in_boundry
) &&
399 (newpos
>= rstrm
->in_base
)) {
400 rstrm
->in_finger
= newpos
;
401 rstrm
->fbtbc
-= delta
;
413 xdrrec_inline(xdrs
, len
)
417 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
420 switch (xdrs
->x_op
) {
423 if ((rstrm
->out_finger
+ len
) <= rstrm
->out_boundry
) {
424 buf
= (int32_t *)(void *)rstrm
->out_finger
;
425 rstrm
->out_finger
+= len
;
430 if ((len
<= (u_int
)rstrm
->fbtbc
) &&
431 ((rstrm
->in_finger
+ len
) <= rstrm
->in_boundry
)) {
432 buf
= (int32_t *)(void *)rstrm
->in_finger
;
434 rstrm
->in_finger
+= len
;
448 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
450 mem_free(rstrm
->out_base
, rstrm
->sendsize
);
451 mem_free(rstrm
->in_base
, rstrm
->recvsize
);
452 mem_free(rstrm
, sizeof(RECSTREAM
));
457 * Exported routines to manage xdr records
461 * Before reading (deserializing from the stream, one should always call
462 * this procedure to guarantee proper record alignment.
465 xdrrec_skiprecord(xdrs
)
468 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
469 enum xprt_stat xstat
;
471 if (rstrm
->nonblock
) {
472 if (__xdrrec_getrec(xdrs
, &xstat
, FALSE
)) {
476 if (rstrm
->in_finger
== rstrm
->in_boundry
&&
477 xstat
== XPRT_MOREREQS
) {
483 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
484 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
487 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
490 rstrm
->last_frag
= FALSE
;
495 * Look ahead fuction.
496 * Returns TRUE iff there is no more input in the buffer
497 * after consuming the rest of the current record.
503 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
505 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
506 if (!skip_input_bytes(rstrm
, rstrm
->fbtbc
))
509 if ((!rstrm
->last_frag
) && (!set_input_fragment(rstrm
)))
512 if (rstrm
->in_finger
== rstrm
->in_boundry
)
518 * The client must tell the package when an end-of-record has occurred.
519 * The second paraemters tells whether the record should be flushed to the
520 * (output) tcp stream. (This let's the package support batched or
521 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
524 xdrrec_endofrecord(xdrs
, sendnow
)
528 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
529 u_long len
; /* fragment length */
531 if (sendnow
|| rstrm
->frag_sent
||
532 ((u_long
)rstrm
->out_finger
+ sizeof(u_int32_t
) >=
533 (u_long
)rstrm
->out_boundry
)) {
534 rstrm
->frag_sent
= FALSE
;
535 return (flush_out(rstrm
, TRUE
));
537 len
= (u_long
)(rstrm
->out_finger
) - (u_long
)(rstrm
->frag_header
) -
539 *(rstrm
->frag_header
) = htonl((u_int32_t
)len
| LAST_FRAG
);
540 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_finger
;
541 rstrm
->out_finger
+= sizeof(u_int32_t
);
546 * Fill the stream buffer with a record for a non-blocking connection.
547 * Return true if a record is available in the buffer, false if not.
550 __xdrrec_getrec(xdrs
, statp
, expectdata
)
552 enum xprt_stat
*statp
;
555 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
559 if (!rstrm
->in_haveheader
) {
560 n
= rstrm
->readit(rstrm
->tcp_handle
, rstrm
->in_hdrp
,
561 (int)sizeof (rstrm
->in_header
) - rstrm
->in_hdrlen
);
563 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
571 rstrm
->in_hdrlen
+= n
;
572 if (rstrm
->in_hdrlen
< (int)sizeof(rstrm
->in_header
)) {
573 *statp
= XPRT_MOREREQS
;
576 rstrm
->in_header
= ntohl(rstrm
->in_header
);
577 fraglen
= (int)(rstrm
->in_header
& ~LAST_FRAG
);
578 if (fraglen
== 0 || fraglen
> rstrm
->in_maxrec
||
579 (rstrm
->in_reclen
+ fraglen
) > rstrm
->in_maxrec
) {
583 rstrm
->in_reclen
+= fraglen
;
584 if ((u_int
)rstrm
->in_reclen
> rstrm
->recvsize
) {
585 if (!realloc_stream(rstrm
, rstrm
->in_reclen
)) {
590 if (rstrm
->in_header
& LAST_FRAG
) {
591 rstrm
->in_header
&= ~LAST_FRAG
;
592 rstrm
->last_frag
= TRUE
;
596 n
= rstrm
->readit(rstrm
->tcp_handle
,
597 rstrm
->in_base
+ rstrm
->in_received
,
598 (rstrm
->in_reclen
- rstrm
->in_received
));
606 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
610 rstrm
->in_received
+= n
;
612 if (rstrm
->in_received
== rstrm
->in_reclen
) {
613 rstrm
->in_haveheader
= FALSE
;
614 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
615 rstrm
->in_hdrlen
= 0;
616 if (rstrm
->last_frag
) {
617 rstrm
->fbtbc
= rstrm
->in_reclen
;
618 rstrm
->in_boundry
= rstrm
->in_base
+ rstrm
->in_reclen
;
619 rstrm
->in_finger
= rstrm
->in_base
;
620 rstrm
->in_reclen
= rstrm
->in_received
= 0;
621 *statp
= XPRT_MOREREQS
;
626 *statp
= XPRT_MOREREQS
;
631 __xdrrec_setnonblock(xdrs
, maxrec
)
635 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
637 rstrm
->nonblock
= TRUE
;
639 maxrec
= rstrm
->recvsize
;
640 rstrm
->in_maxrec
= maxrec
;
646 * Internal useful routines
649 flush_out(rstrm
, eor
)
653 u_int32_t eormask
= (eor
== TRUE
) ? LAST_FRAG
: 0;
654 u_int32_t len
= (u_int32_t
)((u_long
)(rstrm
->out_finger
) -
655 (u_long
)(rstrm
->frag_header
) - sizeof(u_int32_t
));
657 *(rstrm
->frag_header
) = htonl(len
| eormask
);
658 len
= (u_int32_t
)((u_long
)(rstrm
->out_finger
) -
659 (u_long
)(rstrm
->out_base
));
660 if ((*(rstrm
->writeit
))(rstrm
->tcp_handle
, rstrm
->out_base
, (int)len
)
663 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
664 rstrm
->out_finger
= (char *)rstrm
->out_base
+ sizeof(u_int32_t
);
668 static bool_t
/* knows nothing about records! Only about input buffers */
669 fill_input_buf(rstrm
)
678 where
= rstrm
->in_base
;
679 i
= (u_int32_t
)((u_long
)rstrm
->in_boundry
% BYTES_PER_XDR_UNIT
);
681 len
= (u_int32_t
)(rstrm
->in_size
- i
);
682 if ((len
= (*(rstrm
->readit
))(rstrm
->tcp_handle
, where
, len
)) == -1)
684 rstrm
->in_finger
= where
;
686 rstrm
->in_boundry
= where
;
690 static bool_t
/* knows nothing about records! Only about input buffers */
691 get_input_bytes(rstrm
, addr
, len
)
698 if (rstrm
->nonblock
) {
699 if (len
> ((uintptr_t)rstrm
->in_boundry
- (uintptr_t)rstrm
->in_finger
))
701 memcpy(addr
, rstrm
->in_finger
, len
);
702 rstrm
->in_finger
+= len
;
707 current
= ((uintptr_t)rstrm
->in_boundry
-
708 (uintptr_t)rstrm
->in_finger
);
710 if (! fill_input_buf(rstrm
))
714 current
= (len
< current
) ? len
: current
;
715 memmove(addr
, rstrm
->in_finger
, current
);
716 rstrm
->in_finger
+= current
;
723 static bool_t
/* next two bytes of the input stream are treated as a header */
724 set_input_fragment(rstrm
)
731 if (! get_input_bytes(rstrm
, (char *)(void *)&header
, sizeof(header
)))
733 header
= ntohl(header
);
734 rstrm
->last_frag
= ((header
& LAST_FRAG
) == 0) ? FALSE
: TRUE
;
736 * Sanity check. Try not to accept wildly incorrect
737 * record sizes. Unfortunately, the only record size
738 * we can positively identify as being 'wildly incorrect'
739 * is zero. Ridiculously large record sizes may look wrong,
740 * but we don't have any way to be certain that they aren't
741 * what the client actually intended to send us.
745 rstrm
->fbtbc
= header
& (~LAST_FRAG
);
749 static bool_t
/* consumes input bytes; knows nothing about records! */
750 skip_input_bytes(rstrm
, cnt
)
757 current
= (size_t)((long)rstrm
->in_boundry
-
758 (long)rstrm
->in_finger
);
760 if (! fill_input_buf(rstrm
))
764 current
= ((u_int32_t
)cnt
< current
) ? (u_int32_t
)cnt
: current
;
765 rstrm
->in_finger
+= current
;
782 * Reallocate the input buffer for a non-block stream.
785 realloc_stream(rstrm
, size
)
792 if ((u_int
)size
> rstrm
->recvsize
) {
793 buf
= realloc(rstrm
->in_base
, (size_t)size
);
796 diff
= buf
- rstrm
->in_base
;
797 rstrm
->in_finger
+= diff
;
798 rstrm
->in_base
= buf
;
799 rstrm
->in_boundry
= buf
+ size
;
800 rstrm
->recvsize
= size
;
801 rstrm
->in_size
= size
;