1 /* $NetBSD: xdr_rec.c,v 1.36 2015/03/26 11:31:57 justin Exp $ */
4 * Copyright (c) 2010, Oracle America, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of the "Oracle America, Inc." nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 #if defined(LIBC_SCCS) && !defined(lint)
37 static char *sccsid
= "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
38 static char *sccsid
= "@(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC";
40 __RCSID("$NetBSD: xdr_rec.c,v 1.36 2015/03/26 11:31:57 justin Exp $");
45 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
46 * layer above tcp (for rpc's use).
48 * Copyright (C) 1984, Sun Microsystems, Inc.
50 * These routines interface XDRSTREAMS to a tcp/ip connection.
51 * There is a record marking layer between the xdr stream
52 * and the tcp transport level. A record is composed on one or more
53 * record fragments. A record fragment is a thirty-two bit header followed
54 * by n bytes of data, where n is contained in the header. The header
55 * is represented as a htonl(u_long). Thegh order bit encodes
56 * whether or not the fragment is the last fragment of the record
57 * (1 => fragment is last, 0 => more fragments to follow.
58 * The other 31 bits encode the byte length of the fragment.
61 #include "namespace.h"
63 #include <sys/types.h>
65 #include <netinet/in.h>
74 #include <rpc/types.h>
80 #include "rpc_internal.h"
83 __weak_alias(xdrrec_create
,_xdrrec_create
)
84 __weak_alias(xdrrec_endofrecord
,_xdrrec_endofrecord
)
85 __weak_alias(xdrrec_eof
,_xdrrec_eof
)
86 __weak_alias(xdrrec_skiprecord
,_xdrrec_skiprecord
)
89 static bool_t
xdrrec_getlong(XDR
*, long *);
90 static bool_t
xdrrec_putlong(XDR
*, const long *);
91 static bool_t
xdrrec_getbytes(XDR
*, char *, u_int
);
93 static bool_t
xdrrec_putbytes(XDR
*, const char *, u_int
);
94 static u_int
xdrrec_getpos(XDR
*);
95 static bool_t
xdrrec_setpos(XDR
*, u_int
);
96 static int32_t *xdrrec_inline(XDR
*, u_int
);
97 static void xdrrec_destroy(XDR
*);
99 static const struct xdr_ops xdrrec_ops
= {
108 NULL
, /* xdrrec_control */
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 ((uint32_t)(1 << 31))
126 typedef struct rec_strm
{
131 int (*writeit
)(char *, char *, 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 uint32_t *frag_header
; /* beginning of curren fragment */
136 bool_t frag_sent
; /* true if buffer sent in middle of record */
140 int (*readit
)(char *, char *, 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 long 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
*, long);
166 static bool_t
realloc_stream(RECSTREAM
*, 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.
184 /* like read, but pass it a tcp_handle, not sock */
185 int (*readit
)(char *, char *, int),
186 /* like write, but pass it a tcp_handle, not sock */
187 int (*writeit
)(char *, char *, int))
189 RECSTREAM
*rstrm
= mem_alloc(sizeof(RECSTREAM
));
192 warn("%s: out of memory", __func__
);
194 * This is bad. Should rework xdrrec_create to
195 * return a handle, and in this case return NULL
200 rstrm
->sendsize
= sendsize
= fix_buf_size(sendsize
);
201 rstrm
->out_base
= malloc(rstrm
->sendsize
);
202 if (rstrm
->out_base
== NULL
) {
203 warn("%s: out of memory", __func__
);
204 mem_free(rstrm
, sizeof(RECSTREAM
));
208 rstrm
->recvsize
= recvsize
= fix_buf_size(recvsize
);
209 rstrm
->in_base
= malloc(recvsize
);
210 if (rstrm
->in_base
== NULL
) {
211 warn("%s: out of memory", __func__
);
212 mem_free(rstrm
->out_base
, sendsize
);
213 mem_free(rstrm
, sizeof(RECSTREAM
));
219 xdrs
->x_ops
= &xdrrec_ops
;
220 xdrs
->x_private
= rstrm
;
221 rstrm
->tcp_handle
= tcp_handle
;
222 rstrm
->readit
= readit
;
223 rstrm
->writeit
= writeit
;
224 rstrm
->out_finger
= rstrm
->out_boundry
= rstrm
->out_base
;
225 rstrm
->frag_header
= (uint32_t *)(void *)rstrm
->out_base
;
226 rstrm
->out_finger
+= sizeof(uint32_t);
227 rstrm
->out_boundry
+= sendsize
;
228 rstrm
->frag_sent
= FALSE
;
229 rstrm
->in_size
= recvsize
;
230 rstrm
->in_boundry
= rstrm
->in_base
;
231 rstrm
->in_finger
= (rstrm
->in_boundry
+= recvsize
);
233 rstrm
->last_frag
= TRUE
;
234 rstrm
->in_haveheader
= FALSE
;
235 rstrm
->in_hdrlen
= 0;
236 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
237 rstrm
->nonblock
= FALSE
;
238 rstrm
->in_reclen
= 0;
239 rstrm
->in_received
= 0;
244 * The reoutines defined below are the xdr ops which will go into the
245 * xdr handle filled in by xdrrec_create.
249 xdrrec_getlong(XDR
*xdrs
, long *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
>= (long)sizeof(int32_t)) &&
257 (((uintptr_t)rstrm
->in_boundry
- (uintptr_t)buflp
) >= sizeof(int32_t))) {
258 *lp
= (long)ntohl((uint32_t)(*buflp
));
259 rstrm
->fbtbc
-= sizeof(int32_t);
260 rstrm
->in_finger
+= sizeof(int32_t);
262 if (! xdrrec_getbytes(xdrs
, (char *)(void *)&mylong
,
263 (u_int
)sizeof(int32_t)))
265 *lp
= (long)ntohl((uint32_t)mylong
);
271 xdrrec_putlong(XDR
*xdrs
, const long *lp
)
273 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
274 int32_t *dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
276 if ((rstrm
->out_finger
+= sizeof(int32_t)) > rstrm
->out_boundry
) {
278 * this case should almost never happen so the code is
281 rstrm
->out_finger
-= sizeof(int32_t);
282 rstrm
->frag_sent
= TRUE
;
283 if (! flush_out(rstrm
, FALSE
))
285 dest_lp
= ((int32_t *)(void *)(rstrm
->out_finger
));
286 rstrm
->out_finger
+= sizeof(int32_t);
288 *dest_lp
= (int32_t)htonl((uint32_t)(*lp
));
292 static bool_t
/* must manage buffers, fragments, and records */
293 xdrrec_getbytes(XDR
*xdrs
, char *addr
, u_int len
)
295 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
299 current
= (u_int
)rstrm
->fbtbc
;
301 if (rstrm
->last_frag
)
303 if (! set_input_fragment(rstrm
))
307 current
= (len
< current
) ? len
: current
;
308 if (! get_input_bytes(rstrm
, addr
, current
))
311 rstrm
->fbtbc
-= current
;
318 xdrrec_putbytes(XDR
*xdrs
, const char *addr
, u_int len
)
320 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
324 current
= (size_t)((u_long
)rstrm
->out_boundry
-
325 (u_long
)rstrm
->out_finger
);
326 current
= (len
< current
) ? len
: current
;
327 memmove(rstrm
->out_finger
, addr
, current
);
328 rstrm
->out_finger
+= current
;
330 _DIAGASSERT(__type_fit(u_int
, current
));
331 len
-= (u_int
)current
;
332 if (rstrm
->out_finger
== rstrm
->out_boundry
) {
333 rstrm
->frag_sent
= TRUE
;
334 if (! flush_out(rstrm
, FALSE
))
342 xdrrec_getpos(XDR
*xdrs
)
344 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
347 pos
= lseek((int)(u_long
)rstrm
->tcp_handle
, (off_t
)0, 1);
349 switch (xdrs
->x_op
) {
352 pos
+= rstrm
->out_finger
- rstrm
->out_base
;
356 pos
-= rstrm
->in_boundry
- rstrm
->in_finger
;
363 return ((u_int
) pos
);
367 xdrrec_setpos(XDR
*xdrs
, u_int pos
)
369 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
370 u_int currpos
= xdrrec_getpos(xdrs
);
371 int delta
= currpos
- pos
;
374 if ((int)currpos
!= -1)
375 switch (xdrs
->x_op
) {
378 newpos
= rstrm
->out_finger
- delta
;
379 if ((newpos
> (char *)(void *)(rstrm
->frag_header
)) &&
380 (newpos
< rstrm
->out_boundry
)) {
381 rstrm
->out_finger
= newpos
;
387 newpos
= rstrm
->in_finger
- delta
;
388 if ((delta
< (int)(rstrm
->fbtbc
)) &&
389 (newpos
<= rstrm
->in_boundry
) &&
390 (newpos
>= rstrm
->in_base
)) {
391 rstrm
->in_finger
= newpos
;
392 rstrm
->fbtbc
-= delta
;
404 xdrrec_inline(XDR
*xdrs
, u_int len
)
406 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
409 switch (xdrs
->x_op
) {
412 if ((rstrm
->out_finger
+ len
) <= rstrm
->out_boundry
) {
413 buf
= (int32_t *)(void *)rstrm
->out_finger
;
414 rstrm
->out_finger
+= len
;
419 if ((len
<= (u_int
)rstrm
->fbtbc
) &&
420 ((rstrm
->in_finger
+ len
) <= rstrm
->in_boundry
)) {
421 buf
= (int32_t *)(void *)rstrm
->in_finger
;
423 rstrm
->in_finger
+= len
;
434 xdrrec_destroy(XDR
*xdrs
)
436 RECSTREAM
*rstrm
= (RECSTREAM
*)xdrs
->x_private
;
438 mem_free(rstrm
->out_base
, rstrm
->sendsize
);
439 mem_free(rstrm
->in_base
, rstrm
->recvsize
);
440 mem_free(rstrm
, sizeof(RECSTREAM
));
445 * Exported routines to manage xdr records
449 * Before reading (deserializing from the stream, one should always call
450 * this procedure to guarantee proper record alignment.
453 xdrrec_skiprecord(XDR
*xdrs
)
455 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
456 enum xprt_stat xstat
;
458 if (rstrm
->nonblock
) {
459 if (__xdrrec_getrec(xdrs
, &xstat
, FALSE
)) {
463 if (rstrm
->in_finger
== rstrm
->in_boundry
&&
464 xstat
== XPRT_MOREREQS
) {
470 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
471 if (! skip_input_bytes(rstrm
, rstrm
->fbtbc
))
474 if ((! rstrm
->last_frag
) && (! set_input_fragment(rstrm
)))
477 rstrm
->last_frag
= FALSE
;
482 * Look ahead fuction.
483 * Returns TRUE iff there is no more input in the buffer
484 * after consuming the rest of the current record.
487 xdrrec_eof(XDR
*xdrs
)
489 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
491 while (rstrm
->fbtbc
> 0 || (! rstrm
->last_frag
)) {
492 if (!skip_input_bytes(rstrm
, rstrm
->fbtbc
))
495 if ((!rstrm
->last_frag
) && (!set_input_fragment(rstrm
)))
498 if (rstrm
->in_finger
== rstrm
->in_boundry
)
504 * The client must tell the package when an end-of-record has occurred.
505 * The second paraemters tells whether the record should be flushed to the
506 * (output) tcp stream. (This let's the package support batched or
507 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
510 xdrrec_endofrecord(XDR
*xdrs
, int sendnow
)
512 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
513 u_long len
; /* fragment length */
515 if (sendnow
|| rstrm
->frag_sent
||
516 ((u_long
)rstrm
->out_finger
+ sizeof(uint32_t) >=
517 (u_long
)rstrm
->out_boundry
)) {
518 rstrm
->frag_sent
= FALSE
;
519 return (flush_out(rstrm
, TRUE
));
521 len
= (u_long
)(rstrm
->out_finger
) - (u_long
)(rstrm
->frag_header
) -
523 *(rstrm
->frag_header
) = htonl((uint32_t)len
| LAST_FRAG
);
524 rstrm
->frag_header
= (uint32_t *)(void *)rstrm
->out_finger
;
525 rstrm
->out_finger
+= sizeof(uint32_t);
530 * Fill the stream buffer with a record for a non-blocking connection.
531 * Return true if a record is available in the buffer, false if not.
534 __xdrrec_getrec(XDR
*xdrs
, enum xprt_stat
*statp
, bool_t expectdata
)
536 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
540 if (!rstrm
->in_haveheader
) {
541 n
= rstrm
->readit(rstrm
->tcp_handle
, rstrm
->in_hdrp
,
542 (int)sizeof (rstrm
->in_header
) - rstrm
->in_hdrlen
);
544 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
552 _DIAGASSERT(__type_fit(int, n
));
553 rstrm
->in_hdrlen
+= (int)n
;
554 if (rstrm
->in_hdrlen
< (int)sizeof(rstrm
->in_header
)) {
555 *statp
= XPRT_MOREREQS
;
558 rstrm
->in_header
= ntohl(rstrm
->in_header
);
559 fraglen
= (int)(rstrm
->in_header
& ~LAST_FRAG
);
560 if (fraglen
== 0 || fraglen
> rstrm
->in_maxrec
||
561 (rstrm
->in_reclen
+ fraglen
) > rstrm
->in_maxrec
) {
565 rstrm
->in_reclen
+= fraglen
;
566 if ((u_int
)rstrm
->in_reclen
> rstrm
->recvsize
) {
567 if (!realloc_stream(rstrm
, rstrm
->in_reclen
)) {
572 if (rstrm
->in_header
& LAST_FRAG
) {
573 rstrm
->in_header
&= ~LAST_FRAG
;
574 rstrm
->last_frag
= TRUE
;
578 n
= rstrm
->readit(rstrm
->tcp_handle
,
579 rstrm
->in_base
+ rstrm
->in_received
,
580 (rstrm
->in_reclen
- rstrm
->in_received
));
588 *statp
= expectdata
? XPRT_DIED
: XPRT_IDLE
;
592 _DIAGASSERT(__type_fit(int, n
));
593 rstrm
->in_received
+= (int)n
;
595 if (rstrm
->in_received
== rstrm
->in_reclen
) {
596 rstrm
->in_haveheader
= FALSE
;
597 rstrm
->in_hdrp
= (char *)(void *)&rstrm
->in_header
;
598 rstrm
->in_hdrlen
= 0;
599 if (rstrm
->last_frag
) {
600 rstrm
->fbtbc
= rstrm
->in_reclen
;
601 rstrm
->in_boundry
= rstrm
->in_base
+ rstrm
->in_reclen
;
602 rstrm
->in_finger
= rstrm
->in_base
;
603 rstrm
->in_reclen
= rstrm
->in_received
= 0;
604 *statp
= XPRT_MOREREQS
;
609 *statp
= XPRT_MOREREQS
;
614 __xdrrec_setnonblock(XDR
*xdrs
, int maxrec
)
616 RECSTREAM
*rstrm
= (RECSTREAM
*)(xdrs
->x_private
);
618 rstrm
->nonblock
= TRUE
;
620 maxrec
= rstrm
->recvsize
;
621 rstrm
->in_maxrec
= maxrec
;
627 * Internal useful routines
630 flush_out(RECSTREAM
*rstrm
, bool_t eor
)
632 uint32_t eormask
= (eor
== TRUE
) ? LAST_FRAG
: 0;
633 uint32_t len
= (uint32_t)((u_long
)(rstrm
->out_finger
) -
634 (u_long
)(rstrm
->frag_header
) - sizeof(uint32_t));
636 *(rstrm
->frag_header
) = htonl(len
| eormask
);
637 len
= (uint32_t)((u_long
)(rstrm
->out_finger
) -
638 (u_long
)(rstrm
->out_base
));
639 if ((*(rstrm
->writeit
))(rstrm
->tcp_handle
, rstrm
->out_base
, (int)len
)
642 rstrm
->frag_header
= (uint32_t *)(void *)rstrm
->out_base
;
643 rstrm
->out_finger
= (char *)rstrm
->out_base
+ sizeof(uint32_t);
647 static bool_t
/* knows nothing about records! Only about input buffers */
648 fill_input_buf(RECSTREAM
*rstrm
)
656 where
= rstrm
->in_base
;
657 i
= (uint32_t)((u_long
)rstrm
->in_boundry
% BYTES_PER_XDR_UNIT
);
659 len
= (uint32_t)(rstrm
->in_size
- i
);
660 if ((len
= (*(rstrm
->readit
))(rstrm
->tcp_handle
, where
, len
)) == -1)
662 rstrm
->in_finger
= where
;
664 rstrm
->in_boundry
= where
;
668 static bool_t
/* knows nothing about records! Only about input buffers */
669 get_input_bytes(RECSTREAM
*rstrm
, char *addr
, u_int len
)
673 if (rstrm
->nonblock
) {
674 if (len
> ((uintptr_t)rstrm
->in_boundry
- (uintptr_t)rstrm
->in_finger
))
676 memcpy(addr
, rstrm
->in_finger
, len
);
677 rstrm
->in_finger
+= len
;
682 uintptr_t d
= ((uintptr_t)rstrm
->in_boundry
-
683 (uintptr_t)rstrm
->in_finger
);
684 _DIAGASSERT(__type_fit(u_int
, d
));
687 if (! fill_input_buf(rstrm
))
691 current
= (len
< current
) ? len
: current
;
692 memmove(addr
, rstrm
->in_finger
, current
);
693 rstrm
->in_finger
+= current
;
700 static bool_t
/* next two bytes of the input stream are treated as a header */
701 set_input_fragment(RECSTREAM
*rstrm
)
707 if (! get_input_bytes(rstrm
, (char *)(void *)&header
,
708 (u_int
)sizeof(header
)))
710 header
= ntohl(header
);
711 rstrm
->last_frag
= ((header
& LAST_FRAG
) == 0) ? FALSE
: TRUE
;
713 * Sanity check. Try not to accept wildly incorrect
714 * record sizes. Unfortunately, the only record size
715 * we can positively identify as being 'wildly incorrect'
716 * is zero. Ridiculously large record sizes may look wrong,
717 * but we don't have any way to be certain that they aren't
718 * what the client actually intended to send us.
722 rstrm
->fbtbc
= header
& (~LAST_FRAG
);
726 static bool_t
/* consumes input bytes; knows nothing about records! */
727 skip_input_bytes(RECSTREAM
*rstrm
, long cnt
)
732 current
= (uint32_t)((long)rstrm
->in_boundry
-
733 (long)rstrm
->in_finger
);
735 if (! fill_input_buf(rstrm
))
739 current
= ((uint32_t)cnt
< current
) ? (uint32_t)cnt
: current
;
740 rstrm
->in_finger
+= current
;
747 fix_buf_size(u_int s
)
756 * Reallocate the input buffer for a non-block stream.
759 realloc_stream(RECSTREAM
*rstrm
, int size
)
764 if ((u_int
)size
> rstrm
->recvsize
) {
765 buf
= realloc(rstrm
->in_base
, (size_t)size
);
768 diff
= buf
- rstrm
->in_base
;
769 rstrm
->in_finger
+= diff
;
770 rstrm
->in_base
= buf
;
771 rstrm
->in_boundry
= buf
+ size
;
772 rstrm
->recvsize
= size
;
773 rstrm
->in_size
= size
;