xref: /reactos/dll/3rdparty/libtirpc/src/xdr_rec.c (revision c2c66aff)
1 /*
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
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.
15  *
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.
27  */
28 
29 //#include <sys/cdefs.h>
30 //#include <sys/cdefs.h>
31 
32 /*
33  * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
34  * layer above tcp (for rpc's use).
35  *
36  * Copyright (C) 1984, Sun Microsystems, Inc.
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_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.
47  */
48 
49 /* NFSv4.1 client for Windows
50  * Copyright � 2012 The Regents of the University of Michigan
51  *
52  * Olga Kornievskaia <aglo@umich.edu>
53  * Casey Bodley <cbodley@umich.edu>
54  *
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.
59  *
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.
64  *
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
68  */
69 
70 #include <wintirpc.h>
71 #include <io.h>
72 #include <sys/types.h>
73 
74 //#include <netinet/in.h>
75 
76 //#include <err.h>
77 #include <stdio.h>
78 #include <stdlib.h>
79 #include <string.h>
80 
81 #include <rpc/types.h>
82 #include <rpc/xdr.h>
83 #include <rpc/auth.h>
84 #include <rpc/svc_auth.h>
85 #include <rpc/svc.h>
86 #include <rpc/clnt.h>
87 #include <stddef.h>
88 #include "rpc_com.h"
89 //#include <unistd.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);
93 
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 *);
99 
100 static const struct  xdr_ops xdrrec_ops = {
101 	xdrrec_getlong,
102 	xdrrec_putlong,
103 	xdrrec_getbytes,
104 	xdrrec_putbytes,
105 	xdrrec_getpos,
106 	xdrrec_setpos,
107 	xdrrec_inline,
108 	xdrrec_destroy
109 };
110 
111 /*
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).
119  *
120  * The fragment/record machinery is not general;  it is constructed to
121  * meet the needs of xdr and rpc based on tcp.
122  */
123 
124 #define LAST_FRAG ((u_int32_t)(1 << 31))
125 
126 typedef struct rec_strm {
127 	char *tcp_handle;
128 	/*
129 	 * out-goung bits
130 	 */
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 */
137 	/*
138 	 * in-coming bits
139 	 */
140 	int (*readit)(void *, void *, int);
141 	u_long in_size;	/* fixed size of the input buffer */
142 	char *in_base;
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 */
146 	bool_t last_frag;
147 	u_int sendsize;
148 	u_int recvsize;
149 
150 	bool_t nonblock;
151 	bool_t in_haveheader;
152 	u_int32_t in_header;
153 	char *in_hdrp;
154 	u_int in_hdrlen;
155 	u_int in_reclen;
156 	u_int in_received;
157 	u_int in_maxrec;
158 } RECSTREAM;
159 
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);
167 
168 
169 /*
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.
177  */
178 void
xdrrec_create(xdrs,sendsize,recvsize,tcp_handle,readit,writeit)179 xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit)
180 	XDR *xdrs;
181 	u_int sendsize;
182 	u_int recvsize;
183 	void *tcp_handle;
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);
188 {
189 	RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM));
190 
191 	if (rstrm == NULL) {
192 		//warnx("xdrrec_create: out of memory");
193 		/*
194 		 *  This is bad.  Should rework xdrrec_create to
195 		 *  return a handle, and in this case return NULL
196 		 */
197 		return;
198 	}
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));
204 		return;
205 	}
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));
212 		return;
213 	}
214 	/*
215 	 * now the rest ...
216 	 */
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);
230 	rstrm->fbtbc = 0;
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;
238 }
239 
240 
241 /*
242  * The reoutines defined below are the xdr ops which will go into the
243  * xdr handle filled in by xdrrec_create.
244  */
245 
246 static bool_t
xdrrec_getlong(xdrs,lp)247 xdrrec_getlong(xdrs, lp)
248 	XDR *xdrs;
249 	long *lp;
250 {
251 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
252 	int32_t *buflp = (int32_t *)(void *)(rstrm->in_finger);
253 	int32_t mylong;
254 
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);
261 	} else {
262 		if (! xdrrec_getbytes(xdrs, (char *)(void *)&mylong,
263 		    sizeof(int32_t)))
264 			return (FALSE);
265 		*lp = (long)ntohl((u_int32_t)mylong);
266 	}
267 	return (TRUE);
268 }
269 
270 static bool_t
xdrrec_putlong(xdrs,lp)271 xdrrec_putlong(xdrs, lp)
272 	XDR *xdrs;
273 	const long *lp;
274 {
275 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
276 	int32_t *dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
277 
278 	if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
279 		/*
280 		 * this case should almost never happen so the code is
281 		 * inefficient
282 		 */
283 		rstrm->out_finger -= sizeof(int32_t);
284 		rstrm->frag_sent = TRUE;
285 		if (! flush_out(rstrm, FALSE))
286 			return (FALSE);
287 		dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
288 		rstrm->out_finger += sizeof(int32_t);
289 	}
290 	*dest_lp = (int32_t)htonl((u_int32_t)(*lp));
291 	return (TRUE);
292 }
293 
294 static bool_t  /* must manage buffers, fragments, and records */
xdrrec_getbytes(xdrs,addr,len)295 xdrrec_getbytes(xdrs, addr, len)
296 	XDR *xdrs;
297 	char *addr;
298 	u_int len;
299 {
300 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
301 	u_int current;
302 
303 	while (len > 0) {
304 		current = (int)rstrm->fbtbc;
305 		if (current == 0) {
306 			if (rstrm->last_frag)
307 				return (FALSE);
308 			if (! set_input_fragment(rstrm))
309 				return (FALSE);
310 			continue;
311 		}
312 		current = (len < current) ? len : current;
313 		if (! get_input_bytes(rstrm, addr, current))
314 			return (FALSE);
315 		addr += current;
316 		rstrm->fbtbc -= current;
317 		len -= current;
318 	}
319 	return (TRUE);
320 }
321 
322 static bool_t
xdrrec_putbytes(xdrs,addr,len)323 xdrrec_putbytes(xdrs, addr, len)
324 	XDR *xdrs;
325 	const char *addr;
326 	u_int len;
327 {
328 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
329 	size_t current;
330 
331 	while (len > 0) {
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;
337 		addr += current;
338 		len -= current;
339 		if (rstrm->out_finger == rstrm->out_boundry) {
340 			rstrm->frag_sent = TRUE;
341 			if (! flush_out(rstrm, FALSE))
342 				return (FALSE);
343 		}
344 	}
345 	return (TRUE);
346 }
347 
348 static u_int
xdrrec_getpos(xdrs)349 xdrrec_getpos(xdrs)
350 	XDR *xdrs;
351 {
352 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
353 	off_t pos = 0;
354 
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);
357 	if (pos != -1)
358 		switch (xdrs->x_op) {
359 
360 		case XDR_ENCODE:
361 			pos += PtrToLong(rstrm->out_finger) - PtrToLong(rstrm->out_base);
362 			break;
363 
364 		case XDR_DECODE:
365 			pos -= PtrToLong(rstrm->in_boundry) - PtrToLong(rstrm->in_finger);
366 			break;
367 
368 		default:
369 			pos = (off_t) -1;
370 			break;
371 		}
372 	return ((u_int) pos);
373 }
374 
375 static bool_t
xdrrec_setpos(xdrs,pos)376 xdrrec_setpos(xdrs, pos)
377 	XDR *xdrs;
378 	u_int pos;
379 {
380 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
381 	u_int currpos = xdrrec_getpos(xdrs);
382 	int delta = currpos - pos;
383 	char *newpos;
384 
385 	if ((int)currpos != -1)
386 		switch (xdrs->x_op) {
387 
388 		case XDR_ENCODE:
389 			newpos = rstrm->out_finger - delta;
390 			if ((newpos > (char *)(void *)(rstrm->frag_header)) &&
391 				(newpos < rstrm->out_boundry)) {
392 				rstrm->out_finger = newpos;
393 				return (TRUE);
394 			}
395 			break;
396 
397 		case XDR_DECODE:
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;
404 				return (TRUE);
405 			}
406 			break;
407 
408 		case XDR_FREE:
409 			break;
410 		}
411 	return (FALSE);
412 }
413 
414 int32_t *
xdrrec_getoutbase(xdrs)415 xdrrec_getoutbase(xdrs)
416 	XDR *xdrs;
417 {
418 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
419 	int32_t *buf = NULL;
420 
421 	switch (xdrs->x_op) {
422 
423 	case XDR_ENCODE:
424         buf = rstrm->out_base;
425 		break;
426 
427 	case XDR_DECODE:
428 		break;
429 
430 	case XDR_FREE:
431 		break;
432 	}
433 	return (buf);
434 }
435 
436 static int32_t *
xdrrec_inline(xdrs,len)437 xdrrec_inline(xdrs, len)
438 	XDR *xdrs;
439 	u_int len;
440 {
441 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
442 	int32_t *buf = NULL;
443 
444 	switch (xdrs->x_op) {
445 
446 	case XDR_ENCODE:
447 		if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
448 			buf = (int32_t *)(void *)rstrm->out_finger;
449 			rstrm->out_finger += len;
450 		}
451 		break;
452 
453 	case XDR_DECODE:
454 		if ((len <= rstrm->fbtbc) &&
455 			((rstrm->in_finger + len) <= rstrm->in_boundry)) {
456 			buf = (int32_t *)(void *)rstrm->in_finger;
457 			rstrm->fbtbc -= len;
458 			rstrm->in_finger += len;
459 		}
460 		break;
461 
462 	case XDR_FREE:
463 		break;
464 	}
465 	return (buf);
466 }
467 
468 static void
xdrrec_destroy(xdrs)469 xdrrec_destroy(xdrs)
470 	XDR *xdrs;
471 {
472 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
473 
474 	mem_free(rstrm->out_base, rstrm->sendsize);
475 	mem_free(rstrm->in_base, rstrm->recvsize);
476 	mem_free(rstrm, sizeof(RECSTREAM));
477 }
478 
479 
480 /*
481  * Exported routines to manage xdr records
482  */
483 
484 /*
485  * Before reading (deserializing from the stream, one should always call
486  * this procedure to guarantee proper record alignment.
487  */
xdrrec_setlastfrag(xdrs)488 void xdrrec_setlastfrag(xdrs)
489     XDR *xdrs;
490 {
491     RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
492     rstrm->last_frag = 1;
493 }
494 
495 bool_t
xdrrec_skiprecord(xdrs)496 xdrrec_skiprecord(xdrs)
497 	XDR *xdrs;
498 {
499 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
500 	enum xprt_stat xstat;
501 
502 	if (rstrm->nonblock) {
503 		if (__xdrrec_getrec(xdrs, &xstat, FALSE))
504 			return TRUE;
505 
506         if (rstrm->in_finger == rstrm->in_boundry &&
507 		    xstat == XPRT_MOREREQS) {
508 			rstrm->fbtbc = 0;
509 			return TRUE;
510 		}
511 		return FALSE;
512 	}
513 
514 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
515 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
516 			return (FALSE);
517 		rstrm->fbtbc = 0;
518 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
519 			return (FALSE);
520 	}
521 	rstrm->last_frag = FALSE;
522 	return (TRUE);
523 }
524 
525 /*
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.
529  */
530 bool_t
xdrrec_eof(xdrs)531 xdrrec_eof(xdrs)
532 	XDR *xdrs;
533 {
534 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
535 
536 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
537 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
538 			return (TRUE);
539 		rstrm->fbtbc = 0;
540 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
541 			return (TRUE);
542 	}
543 	if (rstrm->in_finger == rstrm->in_boundry)
544 		return (TRUE);
545 	return (FALSE);
546 }
547 
548 /*
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.
553  */
554 bool_t
xdrrec_endofrecord(xdrs,sendnow)555 xdrrec_endofrecord(xdrs, sendnow)
556 	XDR *xdrs;
557 	bool_t sendnow;
558 {
559 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
560 	u_long len;  /* fragment length */
561 
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));
567 	}
568 	len = PtrToUlong(rstrm->out_finger) - PtrToUlong(rstrm->frag_header) -
569 	   sizeof(u_int32_t);
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);
573 	return (TRUE);
574 }
575 
576 /*
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.
579  */
580 bool_t
__xdrrec_getrec(xdrs,statp,expectdata)581 __xdrrec_getrec(xdrs, statp, expectdata)
582 	XDR *xdrs;
583 	enum xprt_stat *statp;
584 	bool_t expectdata;
585 {
586 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
587 	int n;
588 	u_int fraglen;
589 
590 	if (!rstrm->in_haveheader) {
591 		n = rstrm->readit(rstrm->tcp_handle, rstrm->in_hdrp,
592 		    (int)sizeof (rstrm->in_header) - rstrm->in_hdrlen);
593 		if (n == 0) {
594 			*statp = expectdata ? XPRT_DIED : XPRT_IDLE;
595 			return FALSE;
596 		}
597 		if (n < 0) {
598 			*statp = XPRT_DIED;
599 			return FALSE;
600 		}
601 		rstrm->in_hdrp += n;
602 		rstrm->in_hdrlen += n;
603 		if (rstrm->in_hdrlen < sizeof (rstrm->in_header)) {
604 			*statp = XPRT_MOREREQS;
605 			return FALSE;
606 		}
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) {
611 			*statp = XPRT_DIED;
612 			return FALSE;
613 		}
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;
621 		}
622 	}
623 
624     do {
625 	    n =  rstrm->readit(rstrm->tcp_handle,
626 	        rstrm->in_base + rstrm->in_received,
627 	        (rstrm->in_reclen - rstrm->in_received));
628 
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;
632 
633 	    if (n < 0) {
634 		    *statp = XPRT_DIED;
635 		    return FALSE;
636 	    }
637 
638 	    if (n == 0) {
639 		    *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
640 		    return FALSE;
641 	    }
642 
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;
653 			    return TRUE;
654 		    }
655         }
656     } while (1);
657 
658 	*statp = XPRT_MOREREQS;
659 	return FALSE;
660 }
661 
662 bool_t
__xdrrec_setnonblock(xdrs,maxrec)663 __xdrrec_setnonblock(xdrs, maxrec)
664 	XDR *xdrs;
665 	int maxrec;
666 {
667 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
668 
669 	rstrm->nonblock = TRUE;
670 	if (maxrec == 0)
671 		maxrec = rstrm->recvsize;
672 	rstrm->in_maxrec = maxrec;
673 	return TRUE;
674 }
675 
676 bool_t
__xdrrec_setblock(xdrs)677 __xdrrec_setblock(xdrs)
678 	XDR *xdrs;
679 {
680 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
681 
682 	rstrm->nonblock = FALSE;
683 	return TRUE;
684 }
685 /*
686  * Internal useful routines
687  */
688 static bool_t
flush_out(rstrm,eor)689 flush_out(rstrm, eor)
690 	RECSTREAM *rstrm;
691 	bool_t eor;
692 {
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));
696 
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)
701 		!= (int)len)
702 		return (FALSE);
703 	rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
704 	rstrm->out_finger = (char *)rstrm->out_base + sizeof(u_int32_t);
705 	return (TRUE);
706 }
707 
708 static bool_t  /* knows nothing about records!  Only about input buffers */
fill_input_buf(rstrm)709 fill_input_buf(rstrm)
710 	RECSTREAM *rstrm;
711 {
712 	char *where;
713 	u_int32_t i;
714 	int len;
715 
716 	if (rstrm->nonblock)
717 		return FALSE;
718 
719 	where = rstrm->in_base;
720 	i = (u_int32_t)(PtrToUlong(rstrm->in_boundry) % BYTES_PER_XDR_UNIT);
721 	where += i;
722 	len = (u_int32_t)(rstrm->in_size - i);
723 	if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
724 		return (FALSE);
725 	rstrm->in_finger = where;
726 	where += len;
727 	rstrm->in_boundry = where;
728 	return (TRUE);
729 }
730 
731 static bool_t  /* knows nothing about records!  Only about input buffers */
get_input_bytes(rstrm,addr,len)732 get_input_bytes(rstrm, addr, len)
733 	RECSTREAM *rstrm;
734 	char *addr;
735 	u_int len;
736 {
737 	size_t current;
738 
739 	if (rstrm->nonblock) {
740 		if (len > (u_int)(rstrm->in_boundry - rstrm->in_finger))
741 			return FALSE;
742 		memcpy(addr, rstrm->in_finger, (size_t)len);
743 		rstrm->in_finger += len;
744 		return TRUE;
745 	}
746 
747 	while (len > 0) {
748 		current = (size_t)(PtrToLong(rstrm->in_boundry) -
749 		    PtrToLong(rstrm->in_finger));
750 		if (current == 0) {
751 			if (! fill_input_buf(rstrm))
752 				return (FALSE);
753 			continue;
754 		}
755 		current = (len < current) ? len : current;
756 		memmove(addr, rstrm->in_finger, current);
757 		rstrm->in_finger += current;
758 		addr += current;
759 		len -= current;
760 	}
761 	return (TRUE);
762 }
763 
764 static bool_t  /* next two bytes of the input stream are treated as a header */
set_input_fragment(rstrm)765 set_input_fragment(rstrm)
766 	RECSTREAM *rstrm;
767 {
768 	u_int32_t header;
769 
770 	if (rstrm->nonblock)
771 		return FALSE;
772 	if (! get_input_bytes(rstrm, (char *)(void *)&header, sizeof(header)))
773 		return (FALSE);
774 	header = ntohl(header);
775 	rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
776 	/*
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.
783 	 */
784 	if (header == 0)
785 		return(FALSE);
786 	rstrm->fbtbc = header & (~LAST_FRAG);
787 	return (TRUE);
788 }
789 
790 static bool_t  /* consumes input bytes; knows nothing about records! */
skip_input_bytes(rstrm,cnt)791 skip_input_bytes(rstrm, cnt)
792 	RECSTREAM *rstrm;
793 	u_int cnt;
794 {
795 	u_int32_t current;
796 
797 	while (cnt > 0) {
798 		current = (u_int32_t)(PtrToUlong(rstrm->in_boundry) -
799 		    PtrToUlong(rstrm->in_finger));
800 		if (current == 0) {
801 			if (! fill_input_buf(rstrm))
802 				return (FALSE);
803 			continue;
804 		}
805 		current = (u_int32_t)((cnt < current) ? cnt : current);
806 		rstrm->in_finger += current;
807 		cnt -= current;
808 	}
809 	return (TRUE);
810 }
811 
812 static u_int
fix_buf_size(s)813 fix_buf_size(s)
814 	u_int s;
815 {
816 
817 	if (s < 100)
818 		s = 4000;
819 	return (RNDUP(s));
820 }
821 
822 /*
823  * Reallocate the input buffer for a non-block stream.
824  */
825 static bool_t
realloc_stream(rstrm,size)826 realloc_stream(rstrm, size)
827 	RECSTREAM *rstrm;
828 	u_int size;
829 {
830 	ptrdiff_t diff;
831 	char *buf;
832 
833 	if (size > rstrm->recvsize) {
834 		buf = realloc(rstrm->in_base, (size_t)size);
835 		if (buf == NULL)
836 			return FALSE;
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;
843 	}
844 
845 	return TRUE;
846 }
847