xref: /openbsd/lib/libc/rpc/xdr_rec.c (revision 8529ddd3)
1 /*	$OpenBSD: xdr_rec.c,v 1.17 2013/11/26 20:33:08 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2010, Oracle America, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
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.
19  *
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.
32  */
33 
34 /*
35  * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
36  * layer above tcp (for rpc's use).
37  *
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_int32_t).  The high 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.
47  */
48 
49 #include <sys/types.h>
50 #include <netinet/in.h>
51 #include <stddef.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <rpc/types.h>
56 #include <rpc/xdr.h>
57 #include <rpc/auth.h>
58 #include <rpc/clnt.h>
59 #include <rpc/rpc_msg.h>
60 #include <rpc/svc.h>
61 
62 static bool_t	xdrrec_getlong(XDR *, long *);
63 static bool_t	xdrrec_putlong(XDR *, long *);
64 static bool_t	xdrrec_getbytes(XDR *, caddr_t, u_int);
65 static bool_t	xdrrec_putbytes(XDR *, caddr_t, u_int);
66 static u_int	xdrrec_getpos(XDR *);
67 static bool_t	xdrrec_setpos(XDR *, u_int);
68 static int32_t *xdrrec_inline(XDR *, u_int);
69 static void	xdrrec_destroy(XDR *);
70 
71 struct ct_data;
72 
73 static struct  xdr_ops xdrrec_ops = {
74 	xdrrec_getlong,
75 	xdrrec_putlong,
76 	xdrrec_getbytes,
77 	xdrrec_putbytes,
78 	xdrrec_getpos,
79 	xdrrec_setpos,
80 	xdrrec_inline,
81 	xdrrec_destroy,
82 	NULL, /* xdrrec_control */
83 };
84 
85 /*
86  * A record is composed of one or more record fragments.
87  * A record fragment is a four-byte header followed by zero to
88  * 2**32-1 bytes.  The header is treated as a long unsigned and is
89  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
90  * are a byte count of the fragment.  The highest order bit is a boolean:
91  * 1 => this fragment is the last fragment of the record,
92  * 0 => this fragment is followed by more fragment(s).
93  *
94  * The fragment/record machinery is not general;  it is constructed to
95  * meet the needs of xdr and rpc based on tcp.
96  */
97 
98 #define LAST_FRAG ((u_int32_t)(1U << 31))
99 
100 typedef struct rec_strm {
101 	caddr_t tcp_handle;
102 	/*
103 	 * out-goung bits
104 	 */
105 	int (*writeit)(caddr_t, caddr_t, int);
106 	caddr_t out_base;	/* output buffer (points to frag header) */
107 	caddr_t out_finger;	/* next output position */
108 	caddr_t out_boundry;	/* data cannot up to this address */
109 	u_int32_t *frag_header;	/* beginning of current fragment */
110 	bool_t frag_sent;	/* true if buffer sent in middle of record */
111 	/*
112 	 * in-coming bits
113 	 */
114 	int (*readit)(caddr_t, caddr_t, int);
115 	u_long in_size;	/* fixed size of the input buffer */
116 	caddr_t in_base;
117 	caddr_t in_finger;	/* location of next byte to be had */
118 	caddr_t in_boundry;	/* can read up to this location */
119 	long fbtbc;		/* fragment bytes to be consumed */
120 	bool_t last_frag;
121 	u_int sendsize;
122 	u_int recvsize;
123 
124 	bool_t nonblock;
125 	bool_t in_haveheader;
126 	u_int32_t in_header;
127 	char *in_hdrp;
128 	int in_hdrlen;
129 	int in_reclen;
130 	int in_received;
131 	int in_maxrec;
132 } RECSTREAM;
133 
134 static u_int	fix_buf_size(u_int);
135 static bool_t	flush_out(RECSTREAM *, bool_t);
136 static bool_t	fill_input_buf(RECSTREAM *);
137 static bool_t	get_input_bytes(RECSTREAM *, caddr_t, int);
138 static bool_t	set_input_fragment(RECSTREAM *);
139 static bool_t	skip_input_bytes(RECSTREAM *, long);
140 static bool_t	realloc_stream(RECSTREAM *, int);
141 
142 /*
143  * Create an xdr handle for xdrrec
144  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
145  * send and recv buffer sizes (0 => use default).
146  * tcp_handle is an opaque handle that is passed as the first parameter to
147  * the procedures readit and writeit.  Readit and writeit are read and
148  * write respectively.   They are like the system
149  * calls expect that they take an opaque handle rather than an fd.
150  */
151 void
152 xdrrec_create(XDR *xdrs, u_int sendsize, u_int recvsize, caddr_t tcp_handle,
153 	int (*readit)(caddr_t, caddr_t, int),	/* like read, but pass it a
154 						   tcp_handle, not sock */
155 	int (*writeit)(caddr_t, caddr_t, int))	/* like write, but pass it a
156 						   tcp_handle, not sock */
157 {
158 	RECSTREAM *rstrm =
159 		(RECSTREAM *)mem_alloc(sizeof(RECSTREAM));
160 
161 	if (rstrm == NULL) {
162 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
163 		/*
164 		 *  This is bad.  Should rework xdrrec_create to
165 		 *  return a handle, and in this case return NULL
166 		 */
167 		return;
168 	}
169 
170 	rstrm->sendsize = sendsize = fix_buf_size(sendsize);
171 	rstrm->out_base = malloc(rstrm->sendsize);
172 	if (rstrm->out_base == NULL) {
173 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
174 		mem_free(rstrm, sizeof(RECSTREAM));
175 		return;
176 	}
177 
178 	rstrm->recvsize = recvsize = fix_buf_size(recvsize);
179 	rstrm->in_base = malloc(recvsize);
180 	if (rstrm->in_base == NULL) {
181 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
182 		mem_free(rstrm->out_base, sendsize);
183 		mem_free(rstrm, sizeof(RECSTREAM));
184 		return;
185 	}
186 	/*
187 	 * now the rest ...
188 	 */
189 	xdrs->x_ops = &xdrrec_ops;
190 	xdrs->x_private = (caddr_t)rstrm;
191 	rstrm->tcp_handle = tcp_handle;
192 	rstrm->readit = readit;
193 	rstrm->writeit = writeit;
194 	rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
195 	rstrm->frag_header = (u_int32_t *)rstrm->out_base;
196 	rstrm->out_finger += sizeof(u_int32_t);
197 	rstrm->out_boundry += sendsize;
198 	rstrm->frag_sent = FALSE;
199 	rstrm->in_size = recvsize;
200 	rstrm->in_boundry = rstrm->in_base;
201 	rstrm->in_finger = (rstrm->in_boundry += recvsize);
202 	rstrm->fbtbc = 0;
203 	rstrm->last_frag = TRUE;
204 	rstrm->in_haveheader = FALSE;
205 	rstrm->in_hdrlen = 0;
206 	rstrm->in_hdrp = (char *)(void *)&rstrm->in_header;
207 	rstrm->nonblock = FALSE;
208 	rstrm->in_reclen = 0;
209 	rstrm->in_received = 0;
210 }
211 
212 
213 /*
214  * The reoutines defined below are the xdr ops which will go into the
215  * xdr handle filled in by xdrrec_create.
216  */
217 
218 static bool_t
219 xdrrec_getlong(XDR *xdrs, long int *lp)
220 {
221 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
222 	int32_t *buflp = (int32_t *)(rstrm->in_finger);
223 	int32_t mylong;
224 
225 	/* first try the inline, fast case */
226 	if ((rstrm->fbtbc >= sizeof(int32_t)) &&
227 		(((long)rstrm->in_boundry - (long)buflp) >= sizeof(int32_t))) {
228 		*lp = (long)ntohl((u_int32_t)(*buflp));
229 		rstrm->fbtbc -= sizeof(int32_t);
230 		rstrm->in_finger += sizeof(int32_t);
231 	} else {
232 		if (! xdrrec_getbytes(xdrs, (caddr_t)(void *)&mylong,
233 		    sizeof(int32_t)))
234 			return (FALSE);
235 		*lp = (long)ntohl((u_int32_t)mylong);
236 	}
237 	return (TRUE);
238 }
239 
240 static bool_t
241 xdrrec_putlong(XDR *xdrs, long int *lp)
242 {
243 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
244 	int32_t *dest_lp = ((int32_t *)(rstrm->out_finger));
245 
246 	if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
247 		/*
248 		 * this case should almost never happen so the code is
249 		 * inefficient
250 		 */
251 		rstrm->out_finger -= sizeof(int32_t);
252 		rstrm->frag_sent = TRUE;
253 		if (! flush_out(rstrm, FALSE))
254 			return (FALSE);
255 		dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
256 		rstrm->out_finger += sizeof(int32_t);
257 	}
258 	*dest_lp = (int32_t)htonl((u_int32_t)(*lp));
259 	return (TRUE);
260 }
261 
262 static bool_t  /* must manage buffers, fragments, and records */
263 xdrrec_getbytes(XDR *xdrs, caddr_t addr, u_int len)
264 {
265 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
266 	int current;
267 
268 	while (len > 0) {
269 		current = rstrm->fbtbc;
270 		if (current == 0) {
271 			if (rstrm->last_frag)
272 				return (FALSE);
273 			if (! set_input_fragment(rstrm))
274 				return (FALSE);
275 			continue;
276 		}
277 		current = (len < current) ? len : current;
278 		if (! get_input_bytes(rstrm, addr, current))
279 			return (FALSE);
280 		addr += current;
281 		rstrm->fbtbc -= current;
282 		len -= current;
283 	}
284 	return (TRUE);
285 }
286 
287 static bool_t
288 xdrrec_putbytes(XDR *xdrs, caddr_t addr, u_int len)
289 {
290 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
291 	long current;
292 
293 	while (len > 0) {
294 		current = (u_long)rstrm->out_boundry -
295 		    (u_long)rstrm->out_finger;
296 		current = (len < current) ? len : current;
297 		memcpy(rstrm->out_finger, addr, current);
298 		rstrm->out_finger += current;
299 		addr += current;
300 		len -= current;
301 		if (rstrm->out_finger == rstrm->out_boundry) {
302 			rstrm->frag_sent = TRUE;
303 			if (! flush_out(rstrm, FALSE))
304 				return (FALSE);
305 		}
306 	}
307 	return (TRUE);
308 }
309 
310 static u_int
311 xdrrec_getpos(XDR *xdrs)
312 {
313 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
314 	off_t pos;
315 
316 	pos = lseek((int)(long)rstrm->tcp_handle, (off_t)0, SEEK_CUR);
317 	if (pos != (off_t)-1)
318 		switch (xdrs->x_op) {
319 
320 		case XDR_ENCODE:
321 			pos += rstrm->out_finger - rstrm->out_base;
322 			break;
323 
324 		case XDR_DECODE:
325 			pos -= rstrm->in_boundry - rstrm->in_finger;
326 			break;
327 
328 		default:
329 			pos = -1;
330 			break;
331 		}
332 	return ((u_int) pos);
333 }
334 
335 static bool_t
336 xdrrec_setpos(XDR *xdrs, u_int pos)
337 {
338 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
339 	u_int currpos = xdrrec_getpos(xdrs);
340 	int delta = currpos - pos;
341 	caddr_t newpos;
342 
343 	if ((int)currpos != -1)
344 		switch (xdrs->x_op) {
345 
346 		case XDR_ENCODE:
347 			newpos = rstrm->out_finger - delta;
348 			if ((newpos > (caddr_t)(rstrm->frag_header)) &&
349 				(newpos < rstrm->out_boundry)) {
350 				rstrm->out_finger = newpos;
351 				return (TRUE);
352 			}
353 			break;
354 
355 		case XDR_DECODE:
356 			newpos = rstrm->in_finger - delta;
357 			if ((delta < (int)(rstrm->fbtbc)) &&
358 				(newpos <= rstrm->in_boundry) &&
359 				(newpos >= rstrm->in_base)) {
360 				rstrm->in_finger = newpos;
361 				rstrm->fbtbc -= delta;
362 				return (TRUE);
363 			}
364 			break;
365 
366 		case XDR_FREE:
367 			break;
368 		}
369 	return (FALSE);
370 }
371 
372 static int32_t *
373 xdrrec_inline(XDR *xdrs, u_int len)
374 {
375 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
376 	int32_t *buf = NULL;
377 
378 	switch (xdrs->x_op) {
379 
380 	case XDR_ENCODE:
381 		if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
382 			buf = (int32_t *) rstrm->out_finger;
383 			rstrm->out_finger += len;
384 		}
385 		break;
386 
387 	case XDR_DECODE:
388 		if ((len <= rstrm->fbtbc) &&
389 			((rstrm->in_finger + len) <= rstrm->in_boundry)) {
390 			buf = (int32_t *) rstrm->in_finger;
391 			rstrm->fbtbc -= len;
392 			rstrm->in_finger += len;
393 		}
394 		break;
395 
396 	case XDR_FREE:
397 		break;
398 	}
399 	return (buf);
400 }
401 
402 static void
403 xdrrec_destroy(XDR *xdrs)
404 {
405 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
406 
407 	mem_free(rstrm->out_base, rstrm->sendsize);
408 	mem_free(rstrm->in_base, rstrm->recvsize);
409 	mem_free(rstrm, sizeof(RECSTREAM));
410 }
411 
412 bool_t __xdrrec_getrec(XDR *xdrs, enum xprt_stat *statp, bool_t expectdata);
413 
414 /*
415  * Exported routines to manage xdr records
416  */
417 
418 /*
419  * Before reading (deserializing from the stream, one should always call
420  * this procedure to guarantee proper record alignment.
421  */
422 bool_t
423 xdrrec_skiprecord(XDR *xdrs)
424 {
425 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
426 	enum xprt_stat xstat;
427 
428 	if (rstrm->nonblock) {
429 		if (__xdrrec_getrec(xdrs, &xstat, FALSE)) {
430 			rstrm->fbtbc = 0;
431 			return (TRUE);
432 		}
433 		if (rstrm->in_finger == rstrm->in_boundry &&
434 		    xstat == XPRT_MOREREQS) {
435 			rstrm->fbtbc = 0;
436 			return (TRUE);
437 		}
438 		return (FALSE);
439 	}
440 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
441 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
442 			return (FALSE);
443 		rstrm->fbtbc = 0;
444 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
445 			return (FALSE);
446 	}
447 	rstrm->last_frag = FALSE;
448 	return (TRUE);
449 }
450 
451 /*
452  * Look ahead fuction.
453  * Returns TRUE iff there is no more input in the buffer
454  * after consuming the rest of the current record.
455  */
456 bool_t
457 xdrrec_eof(XDR *xdrs)
458 {
459 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
460 
461 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
462 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
463 			return (TRUE);
464 		rstrm->fbtbc = 0;
465 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
466 			return (TRUE);
467 	}
468 	if (rstrm->in_finger == rstrm->in_boundry)
469 		return (TRUE);
470 	return (FALSE);
471 }
472 
473 /*
474  * The client must tell the package when an end-of-record has occurred.
475  * The second paraemters tells whether the record should be flushed to the
476  * (output) tcp stream.  (This let's the package support batched or
477  * pipelined procedure calls.)  TRUE => immmediate flush to tcp connection.
478  */
479 bool_t
480 xdrrec_endofrecord(XDR *xdrs, int32_t sendnow)
481 {
482 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
483 	u_long len;  /* fragment length */
484 
485 	if (sendnow || rstrm->frag_sent ||
486 		((u_long)rstrm->out_finger + sizeof(u_int32_t) >=
487 		(u_long)rstrm->out_boundry)) {
488 		rstrm->frag_sent = FALSE;
489 		return (flush_out(rstrm, TRUE));
490 	}
491 	len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) -
492 	   sizeof(u_int32_t);
493 	*(rstrm->frag_header) = htonl((u_long)len | LAST_FRAG);
494 	rstrm->frag_header = (u_int32_t *)rstrm->out_finger;
495 	rstrm->out_finger += sizeof(u_int32_t);
496 	return (TRUE);
497 }
498 
499 /*
500  * Fill the stream buffer with a record for a non-blocking connection.
501  * Return true if a record is available in the buffer, false if not.
502  */
503 bool_t
504 __xdrrec_getrec(XDR *xdrs, enum xprt_stat *statp, bool_t expectdata)
505 {
506 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
507 	ssize_t n;
508 	int fraglen;
509 
510 	if (!rstrm->in_haveheader) {
511 		n = rstrm->readit(rstrm->tcp_handle, rstrm->in_hdrp,
512 		    (int)sizeof (rstrm->in_header) - rstrm->in_hdrlen);
513 		if (n == 0) {
514 			*statp = expectdata ? XPRT_DIED : XPRT_IDLE;
515 			return (FALSE);
516 		}
517 		if (n < 0) {
518 			*statp = XPRT_DIED;
519 			return (FALSE);
520 		}
521 		rstrm->in_hdrp += n;
522 		rstrm->in_hdrlen += n;
523 		if (rstrm->in_hdrlen < sizeof (rstrm->in_header)) {
524 			*statp = XPRT_MOREREQS;
525 			return (FALSE);
526 		}
527 		rstrm->in_header = ntohl(rstrm->in_header);
528 		fraglen = (int)(rstrm->in_header & ~LAST_FRAG);
529 		if (fraglen == 0 || fraglen > rstrm->in_maxrec ||
530 		    (rstrm->in_reclen + fraglen) > rstrm->in_maxrec) {
531 			*statp = XPRT_DIED;
532 			return (FALSE);
533 		}
534 		rstrm->in_reclen += fraglen;
535 		if (rstrm->in_reclen > rstrm->recvsize)
536 			realloc_stream(rstrm, rstrm->in_reclen);
537 		if (rstrm->in_header & LAST_FRAG) {
538 			rstrm->in_header &= ~LAST_FRAG;
539 			rstrm->last_frag = TRUE;
540 		}
541 	}
542 
543 	n =  rstrm->readit(rstrm->tcp_handle,
544 	    rstrm->in_base + rstrm->in_received,
545 	    (rstrm->in_reclen - rstrm->in_received));
546 
547 	if (n < 0) {
548 		*statp = XPRT_DIED;
549 		return (FALSE);
550 	}
551 
552 	if (n == 0) {
553 		*statp = expectdata ? XPRT_DIED : XPRT_IDLE;
554 		return (FALSE);
555 	}
556 
557 	rstrm->in_received += n;
558 
559 	if (rstrm->in_received == rstrm->in_reclen) {
560 		rstrm->in_haveheader = (FALSE);
561 		rstrm->in_hdrp = (char *)(void *)&rstrm->in_header;
562 		rstrm->in_hdrlen = 0;
563 		if (rstrm->last_frag) {
564 			rstrm->fbtbc = rstrm->in_reclen;
565 			rstrm->in_boundry = rstrm->in_base + rstrm->in_reclen;
566 			rstrm->in_finger = rstrm->in_base;
567 			rstrm->in_reclen = rstrm->in_received = 0;
568 			*statp = XPRT_MOREREQS;
569 			return (TRUE);
570 		}
571 	}
572 
573 	*statp = XPRT_MOREREQS;
574 	return (FALSE);
575 }
576 
577 bool_t
578 __xdrrec_setnonblock(XDR *xdrs, int maxrec)
579 {
580 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
581 
582 	rstrm->nonblock = TRUE;
583 	if (maxrec == 0)
584 		maxrec = rstrm->recvsize;
585 	rstrm->in_maxrec = maxrec;
586 	return (TRUE);
587 }
588 
589 
590 /*
591  * Internal useful routines
592  */
593 static bool_t
594 flush_out(RECSTREAM *rstrm, int32_t eor)
595 {
596 	u_long eormask = (eor == TRUE) ? LAST_FRAG : 0;
597 	u_int32_t len = (u_long)(rstrm->out_finger) -
598 		(u_long)(rstrm->frag_header) - sizeof(u_int32_t);
599 
600 	*(rstrm->frag_header) = htonl(len | eormask);
601 	len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base);
602 	if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
603 		!= (int)len)
604 		return (FALSE);
605 	rstrm->frag_header = (u_int32_t *)rstrm->out_base;
606 	rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(u_int32_t);
607 	return (TRUE);
608 }
609 
610 static bool_t  /* knows nothing about records!  Only about input buffers */
611 fill_input_buf(RECSTREAM *rstrm)
612 {
613 	caddr_t where;
614 	u_long i;
615 	long len;
616 
617 	if (rstrm->nonblock)
618 		return FALSE;
619 	where = rstrm->in_base;
620 	i = (u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
621 	where += i;
622 	len = rstrm->in_size - i;
623 	if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
624 		return (FALSE);
625 	rstrm->in_finger = where;
626 	where += len;
627 	rstrm->in_boundry = where;
628 	return (TRUE);
629 }
630 
631 static bool_t  /* knows nothing about records!  Only about input buffers */
632 get_input_bytes(RECSTREAM *rstrm, caddr_t addr, int len)
633 {
634 	long current;
635 
636 	if (rstrm->nonblock) {
637 		if (len > (int)(rstrm->in_boundry - rstrm->in_finger))
638 			return FALSE;
639 		memcpy(addr, rstrm->in_finger, (size_t)len);
640 		rstrm->in_finger += len;
641 		return (TRUE);
642 	}
643 
644 	while (len > 0) {
645 		current = (long)rstrm->in_boundry - (long)rstrm->in_finger;
646 		if (current == 0) {
647 			if (! fill_input_buf(rstrm))
648 				return (FALSE);
649 			continue;
650 		}
651 		current = (len < current) ? len : current;
652 		memcpy(addr, rstrm->in_finger, current);
653 		rstrm->in_finger += current;
654 		addr += current;
655 		len -= current;
656 	}
657 	return (TRUE);
658 }
659 
660 static bool_t  /* next four bytes of the input stream are treated as a header */
661 set_input_fragment(RECSTREAM *rstrm)
662 {
663 	u_int32_t header;
664 
665 	if (rstrm->nonblock)
666 		return (FALSE);
667 	if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
668 		return (FALSE);
669 	header = (long)ntohl(header);
670 	rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
671 	/*
672 	 * Sanity check. Try not to accept wildly incorrect
673 	 * record sizes. Unfortunately, the only record size
674 	 * we can positively identify as being 'wildly incorrect'
675 	 * is zero. Ridiculously large record sizes may look wrong,
676 	 * but we don't have any way to be certain that they aren't
677 	 * what the client actually intended to send us.
678 	 */
679 	if (header == 0)
680 		return(FALSE);
681 	rstrm->fbtbc = header & (~LAST_FRAG);
682 	return (TRUE);
683 }
684 
685 static bool_t  /* consumes input bytes; knows nothing about records! */
686 skip_input_bytes(RECSTREAM *rstrm, long int cnt)
687 {
688 	long current;
689 
690 	while (cnt > 0) {
691 		current = (long)rstrm->in_boundry - (long)rstrm->in_finger;
692 		if (current == 0) {
693 			if (! fill_input_buf(rstrm))
694 				return (FALSE);
695 			continue;
696 		}
697 		current = (cnt < current) ? cnt : current;
698 		rstrm->in_finger += current;
699 		cnt -= current;
700 	}
701 	return (TRUE);
702 }
703 
704 static u_int
705 fix_buf_size(u_int s)
706 {
707 
708 	if (s < 100)
709 		s = 4000;
710 	return (RNDUP(s));
711 }
712 
713 /*
714  * Reallocate the input buffer for a non-block stream.
715  */
716 static bool_t
717 realloc_stream(RECSTREAM *rstrm, int size)
718 {
719 	ptrdiff_t diff;
720 	char *buf;
721 
722 	if (size > rstrm->recvsize) {
723 		buf = realloc(rstrm->in_base, (size_t)size);
724 		if (buf == NULL)
725 			return (FALSE);
726 		diff = buf - rstrm->in_base;
727 		rstrm->in_finger += diff;
728 		rstrm->in_base = buf;
729 		rstrm->in_boundry = buf + size;
730 		rstrm->recvsize = size;
731 		rstrm->in_size = size;
732 	}
733 
734 	return (TRUE);
735 }
736