3 * Copyright (c) 2009, Sun Microsystems, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * - Neither the name of Sun Microsystems, Inc. nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 #include <sys/cdefs.h>
34 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
35 * layer above tcp (for rpc's use).
37 * Copyright (C) 1984, Sun Microsystems, Inc.
39 * These routines interface XDRSTREAMS to a tcp/ip connection.
40 * There is a record marking layer between the xdr stream
41 * and the tcp transport level. A record is composed on one or more
42 * record fragments. A record fragment is a thirty-two bit header followed
43 * by n bytes of data, where n is contained in the header. The header
44 * is represented as a htonl(u_long). Thegh order bit encodes
45 * whether or not the fragment is the last fragment of the record
46 * (1 => fragment is last, 0 => more fragments to follow.
47 * The other 31 bits encode the byte length of the fragment.
50 #include <sys/types.h>
52 #include <netinet/in.h>
59 #include <rpc/types.h>
62 #include <rpc/svc_auth.h>
68 static bool_t xdrrec_getlong(XDR *, long *);
69 static bool_t xdrrec_putlong(XDR *, const long *);
70 static bool_t xdrrec_getbytes(XDR *, char *, u_int);
72 static bool_t xdrrec_putbytes(XDR *, const char *, u_int);
73 static u_int xdrrec_getpos(XDR *);
74 static bool_t xdrrec_setpos(XDR *, u_int);
75 static int32_t *xdrrec_inline(XDR *, u_int);
76 static void xdrrec_destroy(XDR *);
78 static const struct xdr_ops xdrrec_ops = {
90 * A record is composed of one or more record fragments.
91 * A record fragment is a four-byte header followed by zero to
92 * 2**32-1 bytes. The header is treated as a long unsigned and is
93 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
94 * are a byte count of the fragment. The highest order bit is a boolean:
95 * 1 => this fragment is the last fragment of the record,
96 * 0 => this fragment is followed by more fragment(s).
98 * The fragment/record machinery is not general; it is constructed to
99 * meet the needs of xdr and rpc based on tcp.
102 #define LAST_FRAG ((u_int32_t)(1 << 31))
104 typedef struct rec_strm {
109 int (*writeit)(void *, void *, int);
110 char *out_base; /* output buffer (points to frag header) */
111 char *out_finger; /* next output position */
112 char *out_boundry; /* data cannot up to this address */
113 u_int32_t *frag_header; /* beginning of curren fragment */
114 bool_t frag_sent; /* true if buffer sent in middle of record */
118 int (*readit)(void *, void *, int);
119 u_long in_size; /* fixed size of the input buffer */
121 char *in_finger; /* location of next byte to be had */
122 char *in_boundry; /* can read up to this location */
123 long fbtbc; /* fragment bytes to be consumed */
129 bool_t in_haveheader;
138 static u_int fix_buf_size(u_int);
139 static bool_t flush_out(RECSTREAM *, bool_t);
140 static bool_t fill_input_buf(RECSTREAM *);
141 static bool_t get_input_bytes(RECSTREAM *, char *, int);
142 static bool_t set_input_fragment(RECSTREAM *);
143 static bool_t skip_input_bytes(RECSTREAM *, long);
144 static bool_t realloc_stream(RECSTREAM *, int);
148 * Create an xdr handle for xdrrec
149 * xdrrec_create fills in xdrs. Sendsize and recvsize are
150 * send and recv buffer sizes (0 => use default).
151 * tcp_handle is an opaque handle that is passed as the first parameter to
152 * the procedures readit and writeit. Readit and writeit are read and
153 * write respectively. They are like the system
154 * calls expect that they take an opaque handle rather than an fd.
157 xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit)
162 /* like read, but pass it a tcp_handle, not sock */
163 int (*readit)(void *, void *, int);
164 /* like write, but pass it a tcp_handle, not sock */
165 int (*writeit)(void *, void *, int);
167 RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM));
170 warnx("xdrrec_create: out of memory");
172 * This is bad. Should rework xdrrec_create to
173 * return a handle, and in this case return NULL
177 rstrm->sendsize = sendsize = fix_buf_size(sendsize);
178 rstrm->out_base = mem_alloc(rstrm->sendsize);
179 if (rstrm->out_base == NULL) {
180 warnx("xdrrec_create: out of memory");
181 mem_free(rstrm, sizeof(RECSTREAM));
184 rstrm->recvsize = recvsize = fix_buf_size(recvsize);
185 rstrm->in_base = mem_alloc(recvsize);
186 if (rstrm->in_base == NULL) {
187 warnx("xdrrec_create: out of memory");
188 mem_free(rstrm->out_base, sendsize);
189 mem_free(rstrm, sizeof(RECSTREAM));
195 xdrs->x_ops = &xdrrec_ops;
196 xdrs->x_private = rstrm;
197 rstrm->tcp_handle = tcp_handle;
198 rstrm->readit = readit;
199 rstrm->writeit = writeit;
200 rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
201 rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
202 rstrm->out_finger += sizeof(u_int32_t);
203 rstrm->out_boundry += sendsize;
204 rstrm->frag_sent = FALSE;
205 rstrm->in_size = recvsize;
206 rstrm->in_boundry = rstrm->in_base;
207 rstrm->in_finger = (rstrm->in_boundry += recvsize);
209 rstrm->last_frag = TRUE;
210 rstrm->in_haveheader = FALSE;
211 rstrm->in_hdrlen = 0;
212 rstrm->in_hdrp = (char *)(void *)&rstrm->in_header;
213 rstrm->nonblock = FALSE;
214 rstrm->in_reclen = 0;
215 rstrm->in_received = 0;
220 * The reoutines defined below are the xdr ops which will go into the
221 * xdr handle filled in by xdrrec_create.
225 xdrrec_getlong(xdrs, lp)
229 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
230 int32_t *buflp = (int32_t *)(void *)(rstrm->in_finger);
233 /* first try the inline, fast case */
234 if ((rstrm->fbtbc >= sizeof(int32_t)) &&
235 (((long)rstrm->in_boundry - (long)buflp) >= sizeof(int32_t))) {
236 *lp = (long)ntohl((u_int32_t)(*buflp));
237 rstrm->fbtbc -= sizeof(int32_t);
238 rstrm->in_finger += sizeof(int32_t);
240 if (! xdrrec_getbytes(xdrs, (char *)(void *)&mylong,
243 *lp = (long)ntohl((u_int32_t)mylong);
249 xdrrec_putlong(xdrs, lp)
253 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
254 int32_t *dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
256 if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
258 * this case should almost never happen so the code is
261 rstrm->out_finger -= sizeof(int32_t);
262 rstrm->frag_sent = TRUE;
263 if (! flush_out(rstrm, FALSE))
265 dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
266 rstrm->out_finger += sizeof(int32_t);
268 *dest_lp = (int32_t)htonl((u_int32_t)(*lp));
272 static bool_t /* must manage buffers, fragments, and records */
273 xdrrec_getbytes(xdrs, addr, len)
278 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
282 current = (int)rstrm->fbtbc;
284 if (rstrm->last_frag)
286 if (! set_input_fragment(rstrm))
290 current = (len < current) ? len : current;
291 if (! get_input_bytes(rstrm, addr, current))
294 rstrm->fbtbc -= current;
301 xdrrec_putbytes(xdrs, addr, len)
306 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
310 current = (size_t)((u_long)rstrm->out_boundry -
311 (u_long)rstrm->out_finger);
312 current = (len < current) ? len : current;
313 memmove(rstrm->out_finger, addr, current);
314 rstrm->out_finger += current;
317 if (rstrm->out_finger == rstrm->out_boundry) {
318 rstrm->frag_sent = TRUE;
319 if (! flush_out(rstrm, FALSE))
330 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
333 pos = lseek((int)(u_long)rstrm->tcp_handle, (off_t)0, 1);
335 switch (xdrs->x_op) {
338 pos += rstrm->out_finger - rstrm->out_base;
342 pos -= rstrm->in_boundry - rstrm->in_finger;
349 return ((u_int) pos);
353 xdrrec_setpos(xdrs, pos)
357 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
358 u_int currpos = xdrrec_getpos(xdrs);
359 int delta = currpos - pos;
362 if ((int)currpos != -1)
363 switch (xdrs->x_op) {
366 newpos = rstrm->out_finger - delta;
367 if ((newpos > (char *)(void *)(rstrm->frag_header)) &&
368 (newpos < rstrm->out_boundry)) {
369 rstrm->out_finger = newpos;
375 newpos = rstrm->in_finger - delta;
376 if ((delta < (int)(rstrm->fbtbc)) &&
377 (newpos <= rstrm->in_boundry) &&
378 (newpos >= rstrm->in_base)) {
379 rstrm->in_finger = newpos;
380 rstrm->fbtbc -= delta;
392 xdrrec_inline(xdrs, len)
396 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
399 switch (xdrs->x_op) {
402 if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
403 buf = (int32_t *)(void *)rstrm->out_finger;
404 rstrm->out_finger += len;
409 if ((len <= rstrm->fbtbc) &&
410 ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
411 buf = (int32_t *)(void *)rstrm->in_finger;
413 rstrm->in_finger += len;
427 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
429 mem_free(rstrm->out_base, rstrm->sendsize);
430 mem_free(rstrm->in_base, rstrm->recvsize);
431 mem_free(rstrm, sizeof(RECSTREAM));
436 * Exported routines to manage xdr records
440 * Before reading (deserializing from the stream, one should always call
441 * this procedure to guarantee proper record alignment.
444 xdrrec_skiprecord(xdrs)
447 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
448 enum xprt_stat xstat;
450 if (rstrm->nonblock) {
451 if (__xdrrec_getrec(xdrs, &xstat, FALSE)) {
455 if (rstrm->in_finger == rstrm->in_boundry &&
456 xstat == XPRT_MOREREQS) {
463 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
464 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
467 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
470 rstrm->last_frag = FALSE;
475 * Look ahead function.
476 * Returns TRUE iff there is no more input in the buffer
477 * after consuming the rest of the current record.
483 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
485 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
486 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
489 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
492 if (rstrm->in_finger == rstrm->in_boundry)
498 * The client must tell the package when an end-of-record has occurred.
499 * The second paraemters tells whether the record should be flushed to the
500 * (output) tcp stream. (This let's the package support batched or
501 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
504 xdrrec_endofrecord(xdrs, sendnow)
508 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
509 u_long len; /* fragment length */
511 if (sendnow || rstrm->frag_sent ||
512 ((u_long)rstrm->out_finger + sizeof(u_int32_t) >=
513 (u_long)rstrm->out_boundry)) {
514 rstrm->frag_sent = FALSE;
515 return (flush_out(rstrm, TRUE));
517 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) -
519 *(rstrm->frag_header) = htonl((u_int32_t)len | LAST_FRAG);
520 rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_finger;
521 rstrm->out_finger += sizeof(u_int32_t);
526 * Fill the stream buffer with a record for a non-blocking connection.
527 * Return true if a record is available in the buffer, false if not.
530 __xdrrec_getrec(xdrs, statp, expectdata)
532 enum xprt_stat *statp;
535 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
539 if (!rstrm->in_haveheader) {
540 n = rstrm->readit(rstrm->tcp_handle, rstrm->in_hdrp,
541 (int)sizeof (rstrm->in_header) - rstrm->in_hdrlen);
543 *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
551 rstrm->in_hdrlen += n;
552 if (rstrm->in_hdrlen < sizeof (rstrm->in_header)) {
553 *statp = XPRT_MOREREQS;
556 rstrm->in_header = ntohl(rstrm->in_header);
557 fraglen = (int)(rstrm->in_header & ~LAST_FRAG);
558 if (fraglen == 0 || fraglen > rstrm->in_maxrec ||
559 (rstrm->in_reclen + fraglen) > rstrm->in_maxrec) {
563 rstrm->in_reclen += fraglen;
564 if (rstrm->in_reclen > rstrm->recvsize)
565 realloc_stream(rstrm, rstrm->in_reclen);
566 if (rstrm->in_header & LAST_FRAG) {
567 rstrm->in_header &= ~LAST_FRAG;
568 rstrm->last_frag = TRUE;
572 n = rstrm->readit(rstrm->tcp_handle,
573 rstrm->in_base + rstrm->in_received,
574 (rstrm->in_reclen - rstrm->in_received));
582 *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
586 rstrm->in_received += n;
588 if (rstrm->in_received == rstrm->in_reclen) {
589 rstrm->in_haveheader = FALSE;
590 rstrm->in_hdrp = (char *)(void *)&rstrm->in_header;
591 rstrm->in_hdrlen = 0;
592 if (rstrm->last_frag) {
593 rstrm->fbtbc = rstrm->in_reclen;
594 rstrm->in_boundry = rstrm->in_base + rstrm->in_reclen;
595 rstrm->in_finger = rstrm->in_base;
596 rstrm->in_reclen = rstrm->in_received = 0;
597 *statp = XPRT_MOREREQS;
602 *statp = XPRT_MOREREQS;
607 __xdrrec_setnonblock(xdrs, maxrec)
611 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
613 rstrm->nonblock = TRUE;
615 maxrec = rstrm->recvsize;
616 rstrm->in_maxrec = maxrec;
621 * Internal useful routines
624 flush_out(rstrm, eor)
628 u_int32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
629 u_int32_t len = (u_int32_t)((u_long)(rstrm->out_finger) -
630 (u_long)(rstrm->frag_header) - sizeof(u_int32_t));
632 *(rstrm->frag_header) = htonl(len | eormask);
633 len = (u_int32_t)((u_long)(rstrm->out_finger) -
634 (u_long)(rstrm->out_base));
635 if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
638 rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
639 rstrm->out_finger = (char *)rstrm->out_base + sizeof(u_int32_t);
643 static bool_t /* knows nothing about records! Only about input buffers */
644 fill_input_buf(rstrm)
654 where = rstrm->in_base;
655 i = (u_int32_t)((u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT);
657 len = (u_int32_t)(rstrm->in_size - i);
658 if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
660 rstrm->in_finger = where;
662 rstrm->in_boundry = where;
666 static bool_t /* knows nothing about records! Only about input buffers */
667 get_input_bytes(rstrm, addr, len)
674 if (rstrm->nonblock) {
675 if (len > (int)(rstrm->in_boundry - rstrm->in_finger))
677 memcpy(addr, rstrm->in_finger, (size_t)len);
678 rstrm->in_finger += len;
683 current = (size_t)((long)rstrm->in_boundry -
684 (long)rstrm->in_finger);
686 if (! fill_input_buf(rstrm))
690 current = (len < current) ? len : current;
691 memmove(addr, rstrm->in_finger, current);
692 rstrm->in_finger += current;
699 static bool_t /* next two bytes of the input stream are treated as a header */
700 set_input_fragment(rstrm)
707 if (! get_input_bytes(rstrm, (char *)(void *)&header, sizeof(header)))
709 header = ntohl(header);
710 rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
712 * Sanity check. Try not to accept wildly incorrect
713 * record sizes. Unfortunately, the only record size
714 * we can positively identify as being 'wildly incorrect'
715 * is zero. Ridiculously large record sizes may look wrong,
716 * but we don't have any way to be certain that they aren't
717 * what the client actually intended to send us.
721 rstrm->fbtbc = header & (~LAST_FRAG);
725 static bool_t /* consumes input bytes; knows nothing about records! */
726 skip_input_bytes(rstrm, cnt)
733 current = (size_t)((long)rstrm->in_boundry -
734 (long)rstrm->in_finger);
736 if (! fill_input_buf(rstrm))
740 current = (u_int32_t)((cnt < current) ? cnt : current);
741 rstrm->in_finger += current;
758 * Reallocate the input buffer for a non-block stream.
761 realloc_stream(rstrm, size)
768 if (size > rstrm->recvsize) {
769 buf = realloc(rstrm->in_base, (size_t)size);
772 diff = buf - rstrm->in_base;
773 rstrm->in_finger += diff;
774 rstrm->in_base = buf;
775 rstrm->in_boundry = buf + size;
776 rstrm->recvsize = size;
777 rstrm->in_size = size;