2 * Copyright (c) 2009, Sun Microsystems, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of Sun Microsystems, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
29 //#include <sys/cdefs.h>
30 //#include <sys/cdefs.h>
33 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
34 * layer above tcp (for rpc's use).
36 * Copyright (C) 1984, Sun Microsystems, Inc.
38 * These routines interface XDRSTREAMS to a tcp/ip connection.
39 * There is a record marking layer between the xdr stream
40 * and the tcp transport level. A record is composed on one or more
41 * record fragments. A record fragment is a thirty-two bit header followed
42 * by n bytes of data, where n is contained in the header. The header
43 * is represented as a htonl(u_long). Thegh order bit encodes
44 * whether or not the fragment is the last fragment of the record
45 * (1 => fragment is last, 0 => more fragments to follow.
46 * The other 31 bits encode the byte length of the fragment.
49 /* NFSv4.1 client for Windows
50 * Copyright © 2012 The Regents of the University of Michigan
52 * Olga Kornievskaia <aglo@umich.edu>
53 * Casey Bodley <cbodley@umich.edu>
55 * This library is free software; you can redistribute it and/or modify it
56 * under the terms of the GNU Lesser General Public License as published by
57 * the Free Software Foundation; either version 2.1 of the License, or (at
58 * your option) any later version.
60 * This library is distributed in the hope that it will be useful, but
61 * without any warranty; without even the implied warranty of merchantability
62 * or fitness for a particular purpose. See the GNU Lesser General Public
63 * License for more details.
65 * You should have received a copy of the GNU Lesser General Public License
66 * along with this library; if not, write to the Free Software Foundation,
67 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
72 #include <sys/types.h>
74 //#include <netinet/in.h>
81 #include <rpc/types.h>
84 #include <rpc/svc_auth.h>
90 static bool_t
xdrrec_getlong(XDR
*, long *);
91 static bool_t
xdrrec_putlong(XDR
*, const long *);
92 static bool_t
xdrrec_getbytes(XDR
*, char *, u_int
);
94 static bool_t
xdrrec_putbytes(XDR
*, const char *, u_int
);
95 static u_int
xdrrec_getpos(XDR
*);
96 static bool_t
xdrrec_setpos(XDR
*, u_int
);
97 static int32_t *xdrrec_inline(XDR
*, u_int
);
98 static void xdrrec_destroy(XDR
*);
100 static const struct xdr_ops xdrrec_ops
= {
112 * A record is composed of one or more record fragments.
113 * A record fragment is a four-byte header followed by zero to
114 * 2**32-1 bytes. The header is treated as a long unsigned and is
115 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
116 * are a byte count of the fragment. The highest order bit is a boolean:
117 * 1 => this fragment is the last fragment of the record,
118 * 0 => this fragment is followed by more fragment(s).
120 * The fragment/record machinery is not general; it is constructed to
121 * meet the needs of xdr and rpc based on tcp.
124 #define LAST_FRAG ((u_int32_t)(1 << 31))
126 typedef struct rec_strm
{
131 int (*writeit
)(void *, void *, int);
132 char *out_base
; /* output buffer (points to frag header) */
133 char *out_finger
; /* next output position */
134 char *out_boundry
; /* data cannot up to this address */
135 u_int32_t
*frag_header
; /* beginning of curren fragment */
136 bool_t frag_sent
; /* true if buffer sent in middle of record */
140 int (*readit
)(void *, void *, int);
141 u_long in_size
; /* fixed size of the input buffer */
143 char *in_finger
; /* location of next byte to be had */
144 char *in_boundry
; /* can read up to this location */
145 u_int fbtbc
; /* fragment bytes to be consumed */
151 bool_t in_haveheader
;
160 static u_int
fix_buf_size(u_int
);
161 static bool_t
flush_out(RECSTREAM
*, bool_t
);
162 static bool_t
fill_input_buf(RECSTREAM
*);
163 static bool_t
get_input_bytes(RECSTREAM
*, char *, u_int
);
164 static bool_t
set_input_fragment(RECSTREAM
*);
165 static bool_t
skip_input_bytes(RECSTREAM
*, u_int
);
166 static bool_t
realloc_stream(RECSTREAM
*, u_int
);
170 * Create an xdr handle for xdrrec
171 * xdrrec_create fills in xdrs. Sendsize and recvsize are
172 * send and recv buffer sizes (0 => use default).
173 * tcp_handle is an opaque handle that is passed as the first parameter to
174 * the procedures readit and writeit. Readit and writeit are read and
175 * write respectively. They are like the system
176 * calls expect that they take an opaque handle rather than an fd.
179 xdrrec_create(xdrs
, sendsize
, recvsize
, tcp_handle
, readit
, writeit
)
184 /* like read, but pass it a tcp_handle, not sock */
185 int (*readit
)(void *, void *, int);
186 /* like write, but pass it a tcp_handle, not sock */
187 int (*writeit
)(void *, void *, int);
189 RECSTREAM
*rstrm
= mem_alloc(sizeof(RECSTREAM
));
192 //warnx("xdrrec_create: out of memory");
194 * This is bad. Should rework xdrrec_create to
195 * return a handle, and in this case return NULL
199 rstrm
->sendsize
= sendsize
= fix_buf_size(sendsize
);
200 rstrm
->out_base
= mem_alloc(rstrm
->sendsize
);
201 if (rstrm
->out_base
== NULL
) {
202 //warnx("xdrrec_create: out of memory");
203 mem_free(rstrm
, sizeof(RECSTREAM
));
206 rstrm
->recvsize
= recvsize
= fix_buf_size(recvsize
);
207 rstrm
->in_base
= mem_alloc(recvsize
);
208 if (rstrm
->in_base
== NULL
) {
209 //warnx("xdrrec_create: out of memory");
210 mem_free(rstrm
->out_base
, sendsize
);
211 mem_free(rstrm
, sizeof(RECSTREAM
));
217 xdrs
->x_ops
= &xdrrec_ops
;
218 xdrs
->x_private
= rstrm
;
219 rstrm
->tcp_handle
= tcp_handle
;
220 rstrm
->readit
= readit
;
221 rstrm
->writeit
= writeit
;
222 rstrm
->out_finger
= rstrm
->out_boundry
= rstrm
->out_base
;
223 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
224 rstrm
->out_finger
+= sizeof(u_int32_t
);
225 rstrm
->out_boundry
+= sendsize
;
226 rstrm
->frag_sent
= FALSE
;
227 rstrm
->in_size
= recvsize
;
228 rstrm
->in_boundry
= rstrm
->in_base
;
229 rstrm
->in_finger
= (rstrm
->in_boundry
+= recvsize
);
231 rstrm
->last_frag
= TRUE
;
232 rstrm
->in_haveheader
= FALSE
;
233 rstrm
->in_hdrlen
= 0;
234 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
235 rstrm
->nonblock
= FALSE
;
236 rstrm
->in_reclen
= 0;
237 rstrm
->in_received
= 0;
242 * The reoutines defined below are the xdr ops which will go into the
243 * xdr handle filled in by xdrrec_create.
247 xdrrec_getlong(xdrs
, lp
)
251 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
252 int32_t *buflp
= (int32_t *)(void *)(rstrm
->in_finger
);
255 /* first try the inline, fast case */
256 if ((rstrm
->fbtbc
>= sizeof(int32_t)) &&
257 ((PtrToLong(rstrm
->in_boundry
) - PtrToLong(buflp
)) >= sizeof(int32_t))) {
258 *lp
= (long)ntohl((u_int32_t
)(*buflp
));
259 rstrm
->fbtbc
-= sizeof(int32_t);
260 rstrm
->in_finger
+= sizeof(int32_t);
262 if (! xdrrec_getbytes(xdrs
, (char *)(void *)&mylong
,
265 *lp
= (long)ntohl((u_int32_t
)mylong
);
271 xdrrec_putlong(xdrs
, lp
)
275 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
276 int32_t *dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
278 if ((rstrm
->out_finger
+= sizeof(int32_t)) > rstrm
->out_boundry
) {
280 * this case should almost never happen so the code is
283 rstrm
->out_finger
-= sizeof(int32_t);
284 rstrm
->frag_sent
= TRUE
;
285 if (! flush_out(rstrm
, FALSE
))
287 dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
288 rstrm
->out_finger
+= sizeof(int32_t);
290 *dest_lp
= (int32_t)htonl((u_int32_t
)(*lp
));
294 static bool_t
/* must manage buffers, fragments, and records */
295 xdrrec_getbytes(xdrs
, addr
, len
)
300 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
304 current
= (int)rstrm
->fbtbc
;
306 if (rstrm
->last_frag
)
308 if (! set_input_fragment(rstrm
))
312 current
= (len
< current
) ? len
: current
;
313 if (! get_input_bytes(rstrm
, addr
, current
))
316 rstrm
->fbtbc
-= current
;
323 xdrrec_putbytes(xdrs
, addr
, len
)
328 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
332 current
= (size_t)(PtrToUlong(rstrm
->out_boundry
) -
333 PtrToUlong(rstrm
->out_finger
));
334 current
= (len
< current
) ? len
: current
;
335 memmove(rstrm
->out_finger
, addr
, current
);
336 rstrm
->out_finger
+= current
;
339 if (rstrm
->out_finger
== rstrm
->out_boundry
) {
340 rstrm
->frag_sent
= TRUE
;
341 if (! flush_out(rstrm
, FALSE
))
352 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
355 //pos = lseek((int)(u_long)rstrm->tcp_handle, (off_t)0, 1);
356 //pos = _lseek((int)PtrToUlong(rstrm->tcp_handle), (off_t)0, 1);
358 switch (xdrs
->x_op
) {
361 pos
+= PtrToLong(rstrm
->out_finger
) - PtrToLong(rstrm
->out_base
);
365 pos
-= PtrToLong(rstrm
->in_boundry
) - PtrToLong(rstrm
->in_finger
);
372 return ((u_int
) pos
);
376 xdrrec_setpos(xdrs
, pos
)
380 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
381 u_int currpos
= xdrrec_getpos(xdrs
);
382 int delta
= currpos
- pos
;
385 if ((int)currpos
!= -1)
386 switch (xdrs
->x_op
) {
389 newpos
= rstrm
->out_finger
- delta
;
390 if ((newpos
> (char *)(void *)(rstrm
->frag_header
)) &&
391 (newpos
< rstrm
->out_boundry
)) {
392 rstrm
->out_finger
= newpos
;
398 newpos
= rstrm
->in_finger
- delta
;
399 if ((delta
< (int)(rstrm
->fbtbc
)) &&
400 (newpos
<= rstrm
->in_boundry
) &&
401 (newpos
>= rstrm
->in_base
)) {
402 rstrm
->in_finger
= newpos
;
403 rstrm
->fbtbc
-= delta
;
415 xdrrec_getoutbase(xdrs
)
418 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
421 switch (xdrs
->x_op
) {
424 buf
= rstrm
->out_base
;
437 xdrrec_inline(xdrs
, len
)
441 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
444 switch (xdrs
->x_op
) {
447 if ((rstrm
->out_finger
+ len
) <= rstrm
->out_boundry
) {
448 buf
= (int32_t *)(void *)rstrm
->out_finger
;
449 rstrm
->out_finger
+= len
;
454 if ((len
<= rstrm
->fbtbc
) &&
455 ((rstrm
->in_finger
+ len
) <= rstrm
->in_boundry
)) {
456 buf
= (int32_t *)(void *)rstrm
->in_finger
;
458 rstrm
->in_finger
+= len
;
472 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
474 mem_free(rstrm
->out_base
, rstrm
->sendsize
);
475 mem_free(rstrm
->in_base
, rstrm
->recvsize
);
476 mem_free(rstrm
, sizeof(RECSTREAM
));
481 * Exported routines to manage xdr records
485 * Before reading (deserializing from the stream, one should always call
486 * this procedure to guarantee proper record alignment.
488 void xdrrec_setlastfrag(xdrs
)
491 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
492 rstrm
->last_frag
= 1;
496 xdrrec_skiprecord(xdrs
)
499 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
500 enum xprt_stat xstat
;
502 if (rstrm
->nonblock
) {
503 if (__xdrrec_getrec(xdrs
, &xstat
, FALSE
))
506 if (rstrm
->in_finger
== rstrm
->in_boundry
&&
507 xstat
== XPRT_MOREREQS
) {
514 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
515 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
518 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
521 rstrm
->last_frag
= FALSE
;
526 * Look ahead function.
527 * Returns TRUE iff there is no more input in the buffer
528 * after consuming the rest of the current record.
534 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
536 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
537 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
540 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
543 if (rstrm
->in_finger
== rstrm
->in_boundry
)
549 * The client must tell the package when an end-of-record has occurred.
550 * The second paraemters tells whether the record should be flushed to the
551 * (output) tcp stream. (This let's the package support batched or
552 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
555 xdrrec_endofrecord(xdrs
, sendnow
)
559 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
560 u_long len
; /* fragment length */
562 if (sendnow
|| rstrm
->frag_sent
||
563 (PtrToUlong(rstrm
->out_finger
) + sizeof(u_int32_t
) >=
564 PtrToUlong(rstrm
->out_boundry
))) {
565 rstrm
->frag_sent
= FALSE
;
566 return (flush_out(rstrm
, TRUE
));
568 len
= PtrToUlong(rstrm
->out_finger
) - PtrToUlong(rstrm
->frag_header
) -
570 *(rstrm
->frag_header
) = htonl((u_int32_t
)len
| LAST_FRAG
);
571 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_finger
;
572 rstrm
->out_finger
+= sizeof(u_int32_t
);
577 * Fill the stream buffer with a record for a non-blocking connection.
578 * Return true if a record is available in the buffer, false if not.
581 __xdrrec_getrec(xdrs
, statp
, expectdata
)
583 enum xprt_stat
*statp
;
586 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
590 if (!rstrm
->in_haveheader
) {
591 n
= rstrm
->readit(rstrm
->tcp_handle
, rstrm
->in_hdrp
,
592 (int)sizeof (rstrm
->in_header
) - rstrm
->in_hdrlen
);
594 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
602 rstrm
->in_hdrlen
+= n
;
603 if (rstrm
->in_hdrlen
< sizeof (rstrm
->in_header
)) {
604 *statp
= XPRT_MOREREQS
;
607 rstrm
->in_header
= ntohl(rstrm
->in_header
);
608 fraglen
= (int)(rstrm
->in_header
& ~LAST_FRAG
);
609 if (fraglen
== 0 || fraglen
> rstrm
->in_maxrec
||
610 (rstrm
->in_reclen
+ fraglen
) > rstrm
->in_maxrec
) {
614 rstrm
->fbtbc
= rstrm
->in_header
& (~LAST_FRAG
);
615 rstrm
->in_reclen
+= fraglen
;
616 if (rstrm
->in_reclen
> rstrm
->recvsize
)
617 realloc_stream(rstrm
, rstrm
->in_reclen
);
618 if (rstrm
->in_header
& LAST_FRAG
) {
619 rstrm
->in_header
&= ~LAST_FRAG
;
620 rstrm
->last_frag
= TRUE
;
625 n
= rstrm
->readit(rstrm
->tcp_handle
,
626 rstrm
->in_base
+ rstrm
->in_received
,
627 (rstrm
->in_reclen
- rstrm
->in_received
));
629 /* this case is needed for non-block as socket returns TIMEDOUT and -1
630 * -2 is an error case and covered by the next if() statement */
631 if (n
== -1) continue;
639 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
643 rstrm
->in_received
+= n
;
644 if (rstrm
->in_received
== rstrm
->in_reclen
) {
645 rstrm
->in_haveheader
= FALSE
;
646 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
647 rstrm
->in_hdrlen
= 0;
648 if (rstrm
->last_frag
) {
649 rstrm
->in_boundry
= rstrm
->in_base
+ rstrm
->in_reclen
;
650 rstrm
->in_finger
= rstrm
->in_base
;
651 rstrm
->in_reclen
= rstrm
->in_received
= 0;
652 *statp
= XPRT_MOREREQS
;
658 *statp
= XPRT_MOREREQS
;
663 __xdrrec_setnonblock(xdrs
, maxrec
)
667 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
669 rstrm
->nonblock
= TRUE
;
671 maxrec
= rstrm
->recvsize
;
672 rstrm
->in_maxrec
= maxrec
;
677 __xdrrec_setblock(xdrs
)
680 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
682 rstrm
->nonblock
= FALSE
;
686 * Internal useful routines
689 flush_out(rstrm
, eor
)
693 u_int32_t eormask
= (eor
== TRUE
) ? LAST_FRAG
: 0;
694 u_int32_t len
= (u_int32_t
)(PtrToUlong(rstrm
->out_finger
) -
695 PtrToUlong(rstrm
->frag_header
) - sizeof(u_int32_t
));
697 *(rstrm
->frag_header
) = htonl(len
| eormask
);
698 len
= (u_int32_t
)(PtrToUlong(rstrm
->out_finger
) -
699 PtrToUlong(rstrm
->out_base
));
700 if ((*(rstrm
->writeit
))(rstrm
->tcp_handle
, rstrm
->out_base
, (int)len
)
703 rstrm
->frag_header
= (u_int32_t
*)(void *)rstrm
->out_base
;
704 rstrm
->out_finger
= (char *)rstrm
->out_base
+ sizeof(u_int32_t
);
708 static bool_t
/* knows nothing about records! Only about input buffers */
709 fill_input_buf(rstrm
)
719 where
= rstrm
->in_base
;
720 i
= (u_int32_t
)(PtrToUlong(rstrm
->in_boundry
) % BYTES_PER_XDR_UNIT
);
722 len
= (u_int32_t
)(rstrm
->in_size
- i
);
723 if ((len
= (*(rstrm
->readit
))(rstrm
->tcp_handle
, where
, len
)) == -1)
725 rstrm
->in_finger
= where
;
727 rstrm
->in_boundry
= where
;
731 static bool_t
/* knows nothing about records! Only about input buffers */
732 get_input_bytes(rstrm
, addr
, len
)
739 if (rstrm
->nonblock
) {
740 if (len
> (u_int
)(rstrm
->in_boundry
- rstrm
->in_finger
))
742 memcpy(addr
, rstrm
->in_finger
, (size_t)len
);
743 rstrm
->in_finger
+= len
;
748 current
= (size_t)(PtrToLong(rstrm
->in_boundry
) -
749 PtrToLong(rstrm
->in_finger
));
751 if (! fill_input_buf(rstrm
))
755 current
= (len
< current
) ? len
: current
;
756 memmove(addr
, rstrm
->in_finger
, current
);
757 rstrm
->in_finger
+= current
;
764 static bool_t
/* next two bytes of the input stream are treated as a header */
765 set_input_fragment(rstrm
)
772 if (! get_input_bytes(rstrm
, (char *)(void *)&header
, sizeof(header
)))
774 header
= ntohl(header
);
775 rstrm
->last_frag
= ((header
& LAST_FRAG
) == 0) ? FALSE
: TRUE
;
777 * Sanity check. Try not to accept wildly incorrect
778 * record sizes. Unfortunately, the only record size
779 * we can positively identify as being 'wildly incorrect'
780 * is zero. Ridiculously large record sizes may look wrong,
781 * but we don't have any way to be certain that they aren't
782 * what the client actually intended to send us.
786 rstrm
->fbtbc
= header
& (~LAST_FRAG
);
790 static bool_t
/* consumes input bytes; knows nothing about records! */
791 skip_input_bytes(rstrm
, cnt
)
798 current
= (u_int32_t
)(PtrToUlong(rstrm
->in_boundry
) -
799 PtrToUlong(rstrm
->in_finger
));
801 if (! fill_input_buf(rstrm
))
805 current
= (u_int32_t
)((cnt
< current
) ? cnt
: current
);
806 rstrm
->in_finger
+= current
;
823 * Reallocate the input buffer for a non-block stream.
826 realloc_stream(rstrm
, size
)
833 if (size
> rstrm
->recvsize
) {
834 buf
= realloc(rstrm
->in_base
, (size_t)size
);
837 diff
= buf
- rstrm
->in_base
;
838 rstrm
->in_finger
+= diff
;
839 rstrm
->in_base
= buf
;
840 rstrm
->in_boundry
= buf
+ size
;
841 rstrm
->recvsize
= size
;
842 rstrm
->in_size
= size
;