4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 * Portions of this source code were derived from Berkeley
31 * 4.3 BSD under license from the Regents of the University of
35 #pragma ident "%Z%%M% %I% %E% SMI"
38 * xdr_rec.c, Implements (TCP/IP based) XDR streams with a "record marking"
39 * layer above connection oriented transport layer (e.g. tcp) (for rpc's use).
42 * These routines interface XDRSTREAMS to a (tcp/ip) connection transport.
43 * There is a record marking layer between the xdr stream
44 * and the (tcp) cv 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(ulong_t). The 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.
56 #include <rpc/types.h>
58 #include <sys/types.h>
67 * A record is composed of one or more record fragments.
68 * A record fragment is a four-byte header followed by zero to
69 * 2**32-1 bytes. The header is treated as a long unsigned and is
70 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
71 * are a byte count of the fragment. The highest order bit is a boolean:
72 * 1 => this fragment is the last fragment of the record,
73 * 0 => this fragment is followed by more fragment(s).
75 * The fragment/record machinery is not general; it is constructed to
76 * meet the needs of xdr and rpc based on tcp.
79 #define LAST_FRAG (((uint32_t)1 << 31))
82 * Minimum fragment size is size of rpc callmsg over TCP:
83 * xid direction vers prog vers proc
84 * cred flavor, cred length, cred
85 * verf flavor, verf length, verf
86 * (with no cred or verf allocated)
88 #define MIN_FRAG (10 * BYTES_PER_XDR_UNIT)
90 typedef struct rec_strm
{
96 caddr_t out_base
; /* output buffer (points to frag header) */
97 caddr_t out_finger
; /* next output position */
98 caddr_t out_boundry
; /* data cannot up to this address */
99 uint32_t *frag_header
; /* beginning of current fragment */
100 bool_t frag_sent
; /* true if buffer sent in middle of record */
105 caddr_t in_base
; /* input buffer */
106 caddr_t in_finger
; /* location of next byte to be had */
107 caddr_t in_boundry
; /* can read up to this location */
108 int fbtbc
; /* fragment bytes to be consumed */
113 * Is this the first time that the
114 * getbytes routine has been called ?
118 * Is this non-blocked?
120 uint_t in_nonblock
; /* non-blocked input */
121 uint_t in_needpoll
; /* need to poll to get more data ? */
122 uint32_t in_maxrecsz
; /* maximum record size */
123 caddr_t in_nextrec
; /* start of next record */
124 uint32_t in_nextrecsz
; /* part of next record in buffer */
127 static uint_t
fix_buf_size(uint_t
);
128 static struct xdr_ops
*xdrrec_ops(void);
129 static bool_t
xdrrec_getbytes(XDR
*, caddr_t
, int);
130 static bool_t
flush_out(RECSTREAM
*, bool_t
);
131 static bool_t
get_input_bytes(RECSTREAM
*, caddr_t
, int, bool_t
);
132 static bool_t
set_input_fragment(RECSTREAM
*);
133 static bool_t
skip_input_bytes(RECSTREAM
*, int32_t);
135 bool_t
__xdrrec_getbytes_nonblock(XDR
*, enum xprt_stat
*);
138 * Create an xdr handle for xdrrec
139 * xdrrec_create fills in xdrs. Sendsize and recvsize are
140 * send and recv buffer sizes (0 => use default).
141 * vc_handle is an opaque handle that is passed as the first parameter to
142 * the procedures readit and writeit. Readit and writeit are read and
143 * write respectively. They are like the system calls expect that they
144 * take an opaque handle rather than an fd.
147 static const char mem_err_msg_rec
[] = "xdrrec_create: out of memory";
150 xdrrec_create(XDR
*xdrs
, const uint_t sendsize
, const uint_t recvsize
,
151 const caddr_t tcp_handle
, int (*readit
)(), int (*writeit
)())
153 RECSTREAM
*rstrm
= malloc(sizeof (RECSTREAM
));
156 * XXX: Should still rework xdrrec_create to return a handle,
157 * and in any malloc-failure case return NULL.
160 (void) syslog(LOG_ERR
, mem_err_msg_rec
);
164 * Adjust sizes and allocate buffers; malloc(3C)
165 * provides a buffer suitably aligned for any use, so
166 * there's no need for us to mess around with alignment.
168 * Since non-blocking connections may need to reallocate the input
169 * buffer, we use separate malloc()s for input and output.
171 rstrm
->sendsize
= fix_buf_size(sendsize
);
172 rstrm
->recvsize
= fix_buf_size(recvsize
);
173 rstrm
->out_base
= malloc(rstrm
->sendsize
);
174 if (rstrm
->out_base
== NULL
) {
175 (void) syslog(LOG_ERR
, mem_err_msg_rec
);
179 rstrm
->in_base
= malloc(rstrm
->recvsize
);
180 if (rstrm
->in_base
== NULL
) {
181 (void) syslog(LOG_ERR
, mem_err_msg_rec
);
182 free(rstrm
->out_base
);
191 xdrs
->x_ops
= xdrrec_ops();
192 xdrs
->x_private
= (caddr_t
)rstrm
;
193 rstrm
->tcp_handle
= tcp_handle
;
194 rstrm
->readit
= readit
;
195 rstrm
->writeit
= writeit
;
196 rstrm
->out_finger
= rstrm
->out_boundry
= rstrm
->out_base
;
197 /* LINTED pointer cast */
198 rstrm
->frag_header
= (uint32_t *)rstrm
->out_base
;
199 rstrm
->out_finger
+= sizeof (uint_t
);
200 rstrm
->out_boundry
+= rstrm
->sendsize
;
201 rstrm
->frag_sent
= FALSE
;
202 rstrm
->in_boundry
= rstrm
->in_base
;
203 rstrm
->in_finger
= (rstrm
->in_boundry
+= rstrm
->recvsize
);
205 rstrm
->last_frag
= TRUE
;
206 rstrm
->firsttime
= 0;
207 rstrm
->in_nonblock
= 0;
208 rstrm
->in_needpoll
= 1;
209 rstrm
->in_maxrecsz
= 0;
210 rstrm
->in_nextrec
= rstrm
->in_base
;
211 rstrm
->in_nextrecsz
= 0;
215 * Align input stream. If all applications behaved correctly, this
216 * defensive procedure will not be necessary, since received data will be
220 align_instream(RECSTREAM
*rstrm
)
222 int current
= rstrm
->in_boundry
- rstrm
->in_finger
;
224 (void) memcpy(rstrm
->in_base
, rstrm
->in_finger
, current
);
225 rstrm
->in_finger
= rstrm
->in_base
;
226 rstrm
->in_boundry
= rstrm
->in_finger
+ current
;
230 * The routines defined below are the xdr ops which will go into the
231 * xdr handle filled in by xdrrec_create.
234 xdrrec_getint32(XDR
*xdrs
, int32_t *ip
)
236 /* LINTED pointer cast */
237 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
238 /* LINTED pointer cast */
239 int32_t *buflp
= (int32_t *)(rstrm
->in_finger
);
242 /* first try the inline, fast case */
243 if ((rstrm
->fbtbc
>= (int)sizeof (int32_t)) &&
244 ((uint_t
)(rstrm
->in_boundry
- (caddr_t
)buflp
) >=
245 (uint_t
)sizeof (int32_t))) {
247 * Check if buflp is longword aligned. If not, align it.
249 if (((uintptr_t)buflp
) & ((int)sizeof (int32_t) - 1)) {
250 align_instream(rstrm
);
251 /* LINTED pointer cast */
252 buflp
= (int32_t *)(rstrm
->in_finger
);
254 *ip
= (int32_t)ntohl((uint32_t)(*buflp
));
255 rstrm
->fbtbc
-= (int)sizeof (int32_t);
256 rstrm
->in_finger
+= sizeof (int32_t);
258 if (!xdrrec_getbytes(xdrs
, (caddr_t
)&mylong
, sizeof (int32_t)))
260 *ip
= (int32_t)ntohl((uint32_t)mylong
);
266 xdrrec_putint32(XDR
*xdrs
, int32_t *ip
)
268 /* LINTED pointer cast */
269 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
270 /* LINTED pointer cast */
271 int32_t *dest_lp
= ((int32_t *)(rstrm
->out_finger
));
273 if ((rstrm
->out_finger
+= sizeof (int32_t)) > rstrm
->out_boundry
) {
275 * this case should almost never happen so the code is
278 rstrm
->out_finger
-= sizeof (int32_t);
279 rstrm
->frag_sent
= TRUE
;
280 if (!flush_out(rstrm
, FALSE
))
282 /* LINTED pointer cast */
283 dest_lp
= ((int32_t *)(rstrm
->out_finger
));
284 rstrm
->out_finger
+= sizeof (int32_t);
286 *dest_lp
= (int32_t)htonl((uint32_t)(*ip
));
291 xdrrec_getlong(XDR
*xdrs
, long *lp
)
295 if (!xdrrec_getint32(xdrs
, &i
))
302 xdrrec_putlong(XDR
*xdrs
, long *lp
)
307 if ((*lp
> INT32_MAX
) || (*lp
< INT32_MIN
))
313 return (xdrrec_putint32(xdrs
, &i
));
316 static bool_t
/* must manage buffers, fragments, and records */
317 xdrrec_getbytes(XDR
*xdrs
, caddr_t addr
, int len
)
319 /* LINTED pointer cast */
320 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
324 current
= rstrm
->fbtbc
;
326 if (rstrm
->last_frag
)
328 if (!set_input_fragment(rstrm
))
332 current
= (len
< current
) ? len
: current
;
333 if (!get_input_bytes(rstrm
, addr
, current
, FALSE
))
336 rstrm
->fbtbc
-= current
;
343 xdrrec_putbytes(XDR
*xdrs
, caddr_t addr
, int len
)
345 /* LINTED pointer cast */
346 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
351 current
= (uintptr_t)rstrm
->out_boundry
-
352 (uintptr_t)rstrm
->out_finger
;
353 current
= (len
< current
) ? len
: current
;
354 (void) memcpy(rstrm
->out_finger
, addr
, current
);
355 rstrm
->out_finger
+= current
;
358 if (rstrm
->out_finger
== rstrm
->out_boundry
) {
359 rstrm
->frag_sent
= TRUE
;
360 if (!flush_out(rstrm
, FALSE
))
367 * This is just like the ops vector x_getbytes(), except that
368 * instead of returning success or failure on getting a certain number
369 * of bytes, it behaves much more like the read() system call against a
370 * pipe -- it returns up to the number of bytes requested and a return of
371 * zero indicates end-of-record. A -1 means something very bad happened.
373 uint_t
/* must manage buffers, fragments, and records */
374 xdrrec_readbytes(XDR
*xdrs
, caddr_t addr
, uint_t l
)
376 /* LINTED pointer cast */
377 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
382 current
= rstrm
->fbtbc
;
384 if (rstrm
->last_frag
)
386 if (!set_input_fragment(rstrm
))
390 current
= (len
< current
) ? len
: current
;
391 if (!get_input_bytes(rstrm
, addr
, current
, FALSE
))
394 rstrm
->fbtbc
-= current
;
401 xdrrec_getpos(XDR
*xdrs
)
403 /* LINTED pointer cast */
404 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
407 pos
= lseek((intptr_t)rstrm
->tcp_handle
, 0, 1);
409 switch (xdrs
->x_op
) {
412 pos
+= rstrm
->out_finger
- rstrm
->out_base
;
416 pos
-= rstrm
->in_boundry
- rstrm
->in_finger
;
423 return ((uint_t
)pos
);
427 xdrrec_setpos(XDR
*xdrs
, uint_t pos
)
429 /* LINTED pointer cast */
430 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
431 uint_t currpos
= xdrrec_getpos(xdrs
);
432 int delta
= currpos
- pos
;
435 if ((int)currpos
!= -1)
436 switch (xdrs
->x_op
) {
439 newpos
= rstrm
->out_finger
- delta
;
440 if ((newpos
> (caddr_t
)(rstrm
->frag_header
)) &&
441 (newpos
< rstrm
->out_boundry
)) {
442 rstrm
->out_finger
= newpos
;
448 newpos
= rstrm
->in_finger
- delta
;
449 if ((delta
< (int)(rstrm
->fbtbc
)) &&
450 (newpos
<= rstrm
->in_boundry
) &&
451 (newpos
>= rstrm
->in_base
)) {
452 rstrm
->in_finger
= newpos
;
453 rstrm
->fbtbc
-= delta
;
461 static rpc_inline_t
*
462 xdrrec_inline(XDR
*xdrs
, int len
)
464 /* LINTED pointer cast */
465 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
466 rpc_inline_t
*buf
= NULL
;
468 switch (xdrs
->x_op
) {
471 if ((rstrm
->out_finger
+ len
) <= rstrm
->out_boundry
) {
472 /* LINTED pointer cast */
473 buf
= (rpc_inline_t
*)rstrm
->out_finger
;
474 rstrm
->out_finger
+= len
;
479 if ((len
<= rstrm
->fbtbc
) &&
480 ((rstrm
->in_finger
+ len
) <= rstrm
->in_boundry
)) {
482 * Check if rstrm->in_finger is longword aligned;
485 if (((intptr_t)rstrm
->in_finger
) &
486 (sizeof (int32_t) - 1))
487 align_instream(rstrm
);
488 /* LINTED pointer cast */
489 buf
= (rpc_inline_t
*)rstrm
->in_finger
;
491 rstrm
->in_finger
+= len
;
499 xdrrec_destroy(XDR
*xdrs
)
501 /* LINTED pointer cast */
502 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
504 free(rstrm
->out_base
);
505 free(rstrm
->in_base
);
511 * Exported routines to manage xdr records
515 * Before reading (deserializing) from the stream, one should always call
516 * this procedure to guarantee proper record alignment.
519 xdrrec_skiprecord(XDR
*xdrs
)
521 /* LINTED pointer cast */
522 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
524 if (rstrm
->in_nonblock
) {
525 enum xprt_stat pstat
;
527 * Read and discard a record from the non-blocking
528 * buffer. Return succes only if a complete record can
529 * be retrieved without blocking, or if the buffer was
530 * empty and there was no data to fetch.
532 if (__xdrrec_getbytes_nonblock(xdrs
, &pstat
) ||
533 (pstat
== XPRT_MOREREQS
&&
534 rstrm
->in_finger
== rstrm
->in_boundry
)) {
540 while (rstrm
->fbtbc
> 0 || (!rstrm
->last_frag
)) {
541 if (!skip_input_bytes(rstrm
, rstrm
->fbtbc
))
544 if ((!rstrm
->last_frag
) && (!set_input_fragment(rstrm
)))
547 rstrm
->last_frag
= FALSE
;
552 * Look ahead fuction.
553 * Returns TRUE iff there is no more input in the buffer
554 * after consuming the rest of the current record.
557 xdrrec_eof(XDR
*xdrs
)
559 /* LINTED pointer cast */
560 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
562 if (rstrm
->in_nonblock
) {
564 * If in_needpoll is true, the non-blocking XDR stream
565 * does not have a complete record.
567 return (rstrm
->in_needpoll
);
569 while (rstrm
->fbtbc
> 0 || (!rstrm
->last_frag
)) {
570 if (!skip_input_bytes(rstrm
, rstrm
->fbtbc
))
573 if ((!rstrm
->last_frag
) && (!set_input_fragment(rstrm
)))
576 if (rstrm
->in_finger
== rstrm
->in_boundry
)
582 * The client must tell the package when an end-of-record has occurred.
583 * The second parameters tells whether the record should be flushed to the
584 * (output) tcp stream. (This let's the package support batched or
585 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
588 xdrrec_endofrecord(XDR
*xdrs
, bool_t sendnow
)
590 /* LINTED pointer cast */
591 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
592 uint32_t len
; /* fragment length */
594 if (sendnow
|| rstrm
->frag_sent
||
595 ((uintptr_t)rstrm
->out_finger
+ sizeof (uint32_t) >=
596 (uintptr_t)rstrm
->out_boundry
)) {
597 rstrm
->frag_sent
= FALSE
;
598 return (flush_out(rstrm
, TRUE
));
600 len
= (uintptr_t)(rstrm
->out_finger
) - (uintptr_t)(rstrm
->frag_header
) -
602 *(rstrm
->frag_header
) = htonl((uint32_t)len
| LAST_FRAG
);
603 /* LINTED pointer cast */
604 rstrm
->frag_header
= (uint32_t *)rstrm
->out_finger
;
605 rstrm
->out_finger
+= sizeof (uint32_t);
611 * Internal useful routines
614 flush_out(RECSTREAM
*rstrm
, bool_t eor
)
616 uint32_t eormask
= (eor
== TRUE
) ? LAST_FRAG
: 0;
617 uint32_t len
= (uintptr_t)(rstrm
->out_finger
) -
618 (uintptr_t)(rstrm
->frag_header
) - sizeof (uint32_t);
621 *(rstrm
->frag_header
) = htonl(len
| eormask
);
622 len
= (uintptr_t)(rstrm
->out_finger
) - (uintptr_t)(rstrm
->out_base
);
624 written
= (*(rstrm
->writeit
))
625 (rstrm
->tcp_handle
, rstrm
->out_base
, (int)len
);
627 * Handle the specific 'CANT_STORE' error. In this case, the
628 * fragment must be cleared.
630 if ((written
!= (int)len
) && (written
!= -2))
632 /* LINTED pointer cast */
633 rstrm
->frag_header
= (uint32_t *)rstrm
->out_base
;
634 rstrm
->out_finger
= (caddr_t
)rstrm
->out_base
+ sizeof (uint32_t);
639 /* knows nothing about records! Only about input buffers */
641 fill_input_buf(RECSTREAM
*rstrm
, bool_t do_align
)
646 if (rstrm
->in_nonblock
) {
647 /* Should never get here in the non-blocking case */
650 where
= rstrm
->in_base
;
652 len
= rstrm
->recvsize
;
654 uint_t i
= (uintptr_t)rstrm
->in_boundry
% BYTES_PER_XDR_UNIT
;
657 len
= rstrm
->recvsize
- i
;
659 if ((len
= (*(rstrm
->readit
))(rstrm
->tcp_handle
, where
, len
)) == -1)
661 rstrm
->in_finger
= where
;
663 rstrm
->in_boundry
= where
;
667 /* knows nothing about records! Only about input buffers */
669 get_input_bytes(RECSTREAM
*rstrm
, caddr_t addr
,
670 int len
, bool_t do_align
)
674 if (rstrm
->in_nonblock
) {
676 * Data should already be in the rstrm buffer, so we just
677 * need to copy it to 'addr'.
679 current
= (int)(rstrm
->in_boundry
- rstrm
->in_finger
);
682 (void) memcpy(addr
, rstrm
->in_finger
, len
);
683 rstrm
->in_finger
+= len
;
689 current
= (intptr_t)rstrm
->in_boundry
-
690 (intptr_t)rstrm
->in_finger
;
692 if (!fill_input_buf(rstrm
, do_align
))
696 current
= (len
< current
) ? len
: current
;
697 (void) memcpy(addr
, rstrm
->in_finger
, current
);
698 rstrm
->in_finger
+= current
;
706 /* next four bytes of the input stream are treated as a header */
708 set_input_fragment(RECSTREAM
*rstrm
)
712 if (rstrm
->in_nonblock
) {
714 * In the non-blocking case, the fragment headers should
715 * already have been consumed, so we should never get
716 * here. Might as well return failure right away.
720 if (!get_input_bytes(rstrm
, (caddr_t
)&header
, (int)sizeof (header
),
723 header
= (uint32_t)ntohl(header
);
724 rstrm
->last_frag
= ((header
& LAST_FRAG
) == 0) ? FALSE
: TRUE
;
725 rstrm
->fbtbc
= header
& (~LAST_FRAG
);
729 /* consumes input bytes; knows nothing about records! */
731 skip_input_bytes(RECSTREAM
*rstrm
, int32_t cnt
)
736 current
= (intptr_t)rstrm
->in_boundry
-
737 (intptr_t)rstrm
->in_finger
;
739 if (!fill_input_buf(rstrm
, FALSE
))
743 current
= (cnt
< current
) ? cnt
: current
;
744 rstrm
->in_finger
+= current
;
752 __xdrrec_nonblock_realloc(RECSTREAM
*rstrm
, uint32_t newsize
)
754 caddr_t newbuf
= rstrm
->in_base
;
758 if (newsize
> rstrm
->recvsize
) {
759 newbuf
= (caddr_t
)realloc(newbuf
, newsize
);
763 /* Make pointers valid for the new buffer */
764 offset
= newbuf
- rstrm
->in_base
;
765 rstrm
->in_finger
+= offset
;
766 rstrm
->in_boundry
+= offset
;
767 rstrm
->in_nextrec
+= offset
;
768 rstrm
->in_base
= newbuf
;
769 rstrm
->recvsize
= newsize
;
777 * adjust sizes and allocate buffer quad byte aligned
780 __xdrrec_set_conn_nonblock(XDR
*xdrs
, uint32_t tcp_maxrecsz
)
782 /* LINTED pointer cast */
783 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
786 rstrm
->in_nonblock
= TRUE
;
787 if (tcp_maxrecsz
== 0) {
789 * If maxrecsz has not been set, use the default
790 * that was set from xdrrec_create() and
793 rstrm
->in_maxrecsz
= rstrm
->recvsize
;
796 rstrm
->in_maxrecsz
= tcp_maxrecsz
;
797 if (tcp_maxrecsz
<= rstrm
->recvsize
)
801 * For nonblocked connection, the entire record is read into the
802 * buffer before any xdr processing. This implies that the record
803 * size must allow for the maximum expected message size of the
804 * service. However, it's inconvenient to allocate very large
805 * buffers up front, so we limit ourselves to a reasonable
806 * default size here, and reallocate (up to the maximum record
807 * size allowed for the connection) as necessary.
809 if ((newsize
= tcp_maxrecsz
) > RPC_MAXDATASIZE
) {
810 newsize
= RPC_MAXDATASIZE
;
812 if (!__xdrrec_nonblock_realloc(rstrm
, newsize
)) {
813 (void) syslog(LOG_ERR
, mem_err_msg_rec
);
814 free(rstrm
->out_base
);
815 free(rstrm
->in_base
);
824 * Retrieve input data from the non-blocking connection, increase
825 * the size of the read buffer if necessary, and check that the
826 * record size stays below the allowed maximum for the connection.
829 __xdrrec_getbytes_nonblock(XDR
*xdrs
, enum xprt_stat
*pstat
)
831 /* LINTED pointer cast */
832 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
833 uint32_t prevbytes_thisrec
, minreqrecsize
;
835 int32_t len_received
= 0;
836 uint32_t unprocessed
= 0;
839 * For connection oriented protocols, there's no guarantee that
840 * we will receive the data nicely chopped into records, no
841 * matter how it was sent. We use the in_nextrec pointer to
842 * indicate where in the buffer the next record starts. If
843 * in_nextrec != in_base, there's data in the buffer from
844 * previous reads, and if in_nextrecsz > 0, we need to copy
845 * the portion of the next record already read to the start of
848 if (rstrm
->in_nextrecsz
> 0) {
849 /* Starting on new record with data already in the buffer */
850 (void) memmove(rstrm
->in_base
, rstrm
->in_nextrec
,
851 rstrm
->in_nextrecsz
);
852 rstrm
->in_nextrec
= rstrm
->in_finger
= rstrm
->in_base
;
853 rstrm
->in_boundry
= rstrm
->in_nextrec
+ rstrm
->in_nextrecsz
;
854 unprocessed
= rstrm
->in_nextrecsz
;
855 rstrm
->in_nextrecsz
= 0;
856 } else if (rstrm
->in_nextrec
== rstrm
->in_base
) {
857 /* Starting on new record with empty buffer */
858 rstrm
->in_boundry
= rstrm
->in_finger
= rstrm
->in_base
;
859 rstrm
->last_frag
= FALSE
;
860 rstrm
->in_needpoll
= TRUE
;
863 prevbytes_thisrec
= (uint32_t)(rstrm
->in_boundry
- rstrm
->in_base
);
865 /* Do we need to retrieve data ? */
866 if (rstrm
->in_needpoll
) {
867 int len_requested
, len_total_received
;
869 rstrm
->in_needpoll
= FALSE
;
871 (int)(rstrm
->in_boundry
- rstrm
->in_base
);
872 len_requested
= rstrm
->recvsize
- len_total_received
;
874 * if len_requested is 0, this means that the input
875 * buffer is full and need to be increased.
876 * The minimum record size we will need is whatever's
877 * already in the buffer, plus what's yet to be
878 * consumed in the current fragment, plus space for at
879 * least one more fragment header, if this is not the
880 * last fragment. We use the RNDUP() macro to
881 * account for possible realignment of the next
884 if (len_requested
== 0) {
885 minreqrecsize
= rstrm
->recvsize
+
887 (rstrm
->last_frag
? 0 : sizeof (*header
));
888 minreqrecsize
= RNDUP(minreqrecsize
);
889 if (minreqrecsize
== rstrm
->recvsize
) {
891 * no more bytes to be consumed and
892 * last fragment. We should never end up
893 * here. Might as well return failure
899 if (minreqrecsize
> rstrm
->in_maxrecsz
)
904 if ((len_received
= (*(rstrm
->readit
))(rstrm
->tcp_handle
,
905 rstrm
->in_boundry
, len_requested
)) == -1) {
909 rstrm
->in_boundry
+= len_received
;
910 rstrm
->in_nextrec
= rstrm
->in_boundry
;
913 /* Account for any left over data from previous processing */
914 len_received
+= unprocessed
;
916 /* Set a lower limit on the buffer space we'll need */
917 minreqrecsize
= prevbytes_thisrec
+ rstrm
->fbtbc
;
920 * Consume bytes for this record until it's either complete,
921 * rejected, or we need to poll for more bytes.
923 * If fbtbc == 0, in_finger points to the start of the fragment
924 * header. Otherwise, it points to the start of the fragment data.
926 while (len_received
> 0) {
927 if (rstrm
->fbtbc
== 0) {
928 uint32_t hdrlen
, minfraglen
= 0;
929 uint32_t len_recvd_thisfrag
;
932 len_recvd_thisfrag
= (uint32_t)(rstrm
->in_boundry
-
934 /* LINTED pointer cast */
935 header
= (uint32_t *)rstrm
->in_finger
;
936 hdrlen
= (len_recvd_thisfrag
< sizeof (*header
)) ?
937 len_recvd_thisfrag
: sizeof (*header
);
938 (void) memcpy(&minfraglen
, header
, hdrlen
);
939 last_frag
= (ntohl(minfraglen
) & LAST_FRAG
) != 0;
940 minfraglen
= ntohl(minfraglen
) & (~LAST_FRAG
);
942 * The minimum record size we will need is whatever's
943 * already in the buffer, plus the size of this
944 * fragment, plus (if this isn't the last fragment)
945 * space for at least one more fragment header. We
946 * use the RNDUP() macro to account for possible
947 * realignment of the next fragment header.
949 minreqrecsize
+= minfraglen
+
950 (last_frag
?0:sizeof (*header
));
951 minreqrecsize
= RNDUP(minreqrecsize
);
953 if (hdrlen
< sizeof (*header
)) {
955 * We only have a partial fragment header,
956 * but we can still put a lower limit on the
957 * final fragment size, and check against the
960 if (len_recvd_thisfrag
> 0 &&
961 (minreqrecsize
> rstrm
->in_maxrecsz
)) {
964 /* Need more bytes to obtain fbtbc value */
968 * We've got a complete fragment header, so
969 * 'minfraglen' is the actual fragment length, and
970 * 'minreqrecsize' the requested record size.
972 rstrm
->last_frag
= last_frag
;
973 rstrm
->fbtbc
= minfraglen
;
975 * Check that the sum of the total number of bytes read
976 * so far (for the record) and the size of the incoming
977 * fragment is less than the maximum allowed.
979 * If this is the last fragment, also check that the
980 * record (message) meets the minimum length
983 * If this isn't the last fragment, check for a zero
984 * fragment length. Accepting such fragments would
985 * leave us open to an attack where the sender keeps
986 * the connection open indefinitely, without any
987 * progress, by occasionally sending a zero length
990 if ((minreqrecsize
> rstrm
->in_maxrecsz
) ||
991 (rstrm
->last_frag
&& minreqrecsize
< MIN_FRAG
) ||
992 (!rstrm
->last_frag
&& minfraglen
== 0)) {
995 rstrm
->last_frag
= 1;
1000 * Make this fragment abut the previous one. If it's
1001 * the first fragment, just advance in_finger past
1002 * the header. This avoids buffer copying for the
1003 * usual case where there's one fragment per record.
1005 if (rstrm
->in_finger
== rstrm
->in_base
) {
1006 rstrm
->in_finger
+= sizeof (*header
);
1008 rstrm
->in_boundry
-= sizeof (*header
);
1009 (void) memmove(rstrm
->in_finger
,
1010 rstrm
->in_finger
+ sizeof (*header
),
1011 rstrm
->in_boundry
- rstrm
->in_finger
);
1013 /* Consume the fragment header */
1014 if (len_received
> sizeof (*header
)) {
1015 len_received
-= sizeof (*header
);
1021 * Consume whatever fragment bytes we have.
1022 * If we've received all bytes for this fragment, advance
1023 * in_finger to point to the start of the next fragment
1024 * header. Otherwise, make fbtbc tell how much is left in
1025 * in this fragment and advance finger to point to end of
1028 if (len_received
>= rstrm
->fbtbc
) {
1029 len_received
-= rstrm
->fbtbc
;
1030 rstrm
->in_finger
+= rstrm
->fbtbc
;
1033 rstrm
->fbtbc
-= len_received
;
1034 rstrm
->in_finger
+= len_received
;
1038 * If there's more data in the buffer, there are two
1041 * (1) This is the last fragment, so the extra data
1042 * presumably belongs to the next record.
1044 * (2) Not the last fragment, so we'll start over
1045 * from the top of the loop.
1047 if (len_received
> 0 && rstrm
->last_frag
) {
1048 rstrm
->in_nextrec
= rstrm
->in_finger
;
1049 rstrm
->in_nextrecsz
= (uint32_t)(rstrm
->in_boundry
-
1055 /* Was this the last fragment, and have we read the entire record ? */
1056 if (rstrm
->last_frag
&& rstrm
->fbtbc
== 0) {
1057 *pstat
= XPRT_MOREREQS
;
1059 * We've been using both in_finger and fbtbc for our own
1060 * purposes. Now's the time to update them to be what
1061 * xdrrec_inline() expects. Set in_finger to point to the
1062 * start of data for this record, and fbtbc to the number
1063 * of bytes in the record.
1065 rstrm
->fbtbc
= (int)(rstrm
->in_finger
-
1066 rstrm
->in_base
- sizeof (*header
));
1067 rstrm
->in_finger
= rstrm
->in_base
+ sizeof (*header
);
1068 if (rstrm
->in_nextrecsz
== 0)
1069 rstrm
->in_nextrec
= rstrm
->in_base
;
1074 * Need more bytes, so we set the needpoll flag, and go back to
1075 * the main RPC request loop. However, first we reallocate the
1076 * input buffer, if necessary.
1078 if (minreqrecsize
> rstrm
->recvsize
) {
1079 if (!__xdrrec_nonblock_realloc(rstrm
, minreqrecsize
)) {
1081 rstrm
->last_frag
= 1;
1087 rstrm
->in_needpoll
= TRUE
;
1088 *pstat
= XPRT_MOREREQS
;
1093 __is_xdrrec_first(XDR
*xdrs
)
1095 /* LINTED pointer cast */
1096 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
1097 return ((rstrm
->firsttime
== TRUE
) ? 1 : 0);
1101 __xdrrec_setfirst(XDR
*xdrs
)
1103 /* LINTED pointer cast */
1104 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
1107 * Set rstrm->firsttime only if the input buffer is empty.
1108 * Otherwise, the first read from the network could skip
1111 if (rstrm
->in_finger
== rstrm
->in_boundry
)
1112 rstrm
->firsttime
= TRUE
;
1114 rstrm
->firsttime
= FALSE
;
1119 __xdrrec_resetfirst(XDR
*xdrs
)
1121 /* LINTED pointer cast */
1122 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
1124 rstrm
->firsttime
= FALSE
;
1130 fix_buf_size(uint_t s
)
1140 xdrrec_control(XDR
*xdrs
, int request
, void *info
)
1142 /* LINTED pointer cast */
1143 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
1148 case XDR_GET_BYTES_AVAIL
:
1149 /* Check if at end of fragment and not last fragment */
1150 if ((rstrm
->fbtbc
== 0) && (!rstrm
->last_frag
))
1151 if (!set_input_fragment(rstrm
)) {
1155 xptr
= (xdr_bytesrec
*)info
;
1156 xptr
->xc_is_last_record
= rstrm
->last_frag
;
1157 xptr
->xc_num_avail
= rstrm
->fbtbc
;
1167 static struct xdr_ops
*
1170 static struct xdr_ops ops
;
1171 extern mutex_t ops_lock
;
1173 /* VARIABLES PROTECTED BY ops_lock: ops */
1175 (void) mutex_lock(&ops_lock
);
1176 if (ops
.x_getlong
== NULL
) {
1177 ops
.x_getlong
= xdrrec_getlong
;
1178 ops
.x_putlong
= xdrrec_putlong
;
1179 ops
.x_getbytes
= xdrrec_getbytes
;
1180 ops
.x_putbytes
= xdrrec_putbytes
;
1181 ops
.x_getpostn
= xdrrec_getpos
;
1182 ops
.x_setpostn
= xdrrec_setpos
;
1183 ops
.x_inline
= xdrrec_inline
;
1184 ops
.x_destroy
= xdrrec_destroy
;
1185 ops
.x_control
= xdrrec_control
;
1187 ops
.x_getint32
= xdrrec_getint32
;
1188 ops
.x_putint32
= xdrrec_putint32
;
1191 (void) mutex_unlock(&ops_lock
);