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