xref: /illumos-gate/usr/src/lib/libnsl/rpc/xdr_rec.c (revision b02947bf393d39f68e6c7fa8ccb98688f7f9c407)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 /*
30  * Portions of this source code were derived from Berkeley
31  * 4.3 BSD under license from the Regents of the University of
32  * California.
33  */
34 
35 /*
36  * xdr_rec.c, Implements (TCP/IP based) XDR streams with a "record marking"
37  * layer above connection oriented transport layer (e.g. tcp) (for rpc's use).
38  *
39  *
40  * These routines interface XDRSTREAMS to a (tcp/ip) connection transport.
41  * There is a record marking layer between the xdr stream
42  * and the (tcp) cv transport level.  A record is composed on one or more
43  * record fragments.  A record fragment is a thirty-two bit header followed
44  * by n bytes of data, where n is contained in the header.  The header
45  * is represented as a htonl(ulong_t).  The order bit encodes
46  * whether or not the fragment is the last fragment of the record
47  * (1 => fragment is last, 0 => more fragments to follow.
48  * The other 31 bits encode the byte length of the fragment.
49  */
50 
51 #include "mt.h"
52 #include "rpc_mt.h"
53 #include <stdio.h>
54 #include <rpc/types.h>
55 #include <rpc/rpc.h>
56 #include <sys/types.h>
57 #include <syslog.h>
58 #include <memory.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <inttypes.h>
62 #include <string.h>
63 
64 /*
65  * A record is composed of one or more record fragments.
66  * A record fragment is a four-byte header followed by zero to
67  * 2**32-1 bytes.  The header is treated as a long unsigned and is
68  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
69  * are a byte count of the fragment.  The highest order bit is a boolean:
70  * 1 => this fragment is the last fragment of the record,
71  * 0 => this fragment is followed by more fragment(s).
72  *
73  * The fragment/record machinery is not general;  it is constructed to
74  * meet the needs of xdr and rpc based on tcp.
75  */
76 
77 #define	LAST_FRAG (((uint32_t)1 << 31))
78 
79 /*
80  * Minimum fragment size is size of rpc callmsg over TCP:
81  * xid direction vers prog vers proc
82  *   cred flavor, cred length, cred
83  *   verf flavor, verf length, verf
84  *   (with no cred or verf allocated)
85  */
86 #define	MIN_FRAG	(10 * BYTES_PER_XDR_UNIT)
87 
88 typedef struct rec_strm {
89 	caddr_t tcp_handle;
90 	/*
91 	 * out-going bits
92 	 */
93 	int (*writeit)();
94 	caddr_t out_base;	/* output buffer (points to frag header) */
95 	caddr_t out_finger;	/* next output position */
96 	caddr_t out_boundry;	/* data cannot up to this address */
97 	uint32_t *frag_header;	/* beginning of current fragment */
98 	bool_t frag_sent;	/* true if buffer sent in middle of record */
99 	/*
100 	 * in-coming bits
101 	 */
102 	int (*readit)();
103 	caddr_t in_base;	/* input buffer */
104 	caddr_t in_finger;	/* location of next byte to be had */
105 	caddr_t in_boundry;	/* can read up to this location */
106 	int fbtbc;		/* fragment bytes to be consumed */
107 	bool_t last_frag;
108 	uint_t sendsize;
109 	uint_t recvsize;
110 	/*
111 	 * Is this the first time that the
112 	 * getbytes routine has been called ?
113 	 */
114 	uint_t firsttime;
115 	/*
116 	 * Is this non-blocked?
117 	 */
118 	uint_t in_nonblock;	/* non-blocked input */
119 	uint_t in_needpoll;	/* need to poll to get more data ? */
120 	uint32_t in_maxrecsz;	/* maximum record size */
121 	caddr_t in_nextrec;	/* start of next record */
122 	uint32_t in_nextrecsz;	/* part of next record in buffer */
123 } RECSTREAM;
124 
125 static uint_t	fix_buf_size(uint_t);
126 static struct	xdr_ops *xdrrec_ops(void);
127 static bool_t	xdrrec_getbytes(XDR *, caddr_t, int);
128 static bool_t	flush_out(RECSTREAM *, bool_t);
129 static bool_t	get_input_bytes(RECSTREAM *, caddr_t, int, bool_t);
130 static bool_t	set_input_fragment(RECSTREAM *);
131 static bool_t	skip_input_bytes(RECSTREAM *, int32_t);
132 
133 bool_t		__xdrrec_getbytes_nonblock(XDR *, enum xprt_stat *);
134 
135 /*
136  * Create an xdr handle for xdrrec
137  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
138  * send and recv buffer sizes (0 => use default).
139  * vc_handle is an opaque handle that is passed as the first parameter to
140  * the procedures readit and writeit.  Readit and writeit are read and
141  * write respectively. They are like the system calls expect that they
142  * take an opaque handle rather than an fd.
143  */
144 
145 static const char mem_err_msg_rec[] = "xdrrec_create: out of memory";
146 
147 void
148 xdrrec_create(XDR *xdrs, const uint_t sendsize, const uint_t recvsize,
149     const caddr_t tcp_handle, int (*readit)(), int (*writeit)())
150 {
151 	RECSTREAM *rstrm = malloc(sizeof (RECSTREAM));
152 
153 	/*
154 	 * XXX: Should still rework xdrrec_create to return a handle,
155 	 * and in any malloc-failure case return NULL.
156 	 */
157 	if (rstrm == NULL) {
158 		(void) syslog(LOG_ERR, mem_err_msg_rec);
159 		return;
160 	}
161 	/*
162 	 * Adjust sizes and allocate buffers; malloc(3C)
163 	 * provides a buffer suitably aligned for any use, so
164 	 * there's no need for us to mess around with alignment.
165 	 *
166 	 * Since non-blocking connections may need to reallocate the input
167 	 * buffer, we use separate malloc()s for input and output.
168 	 */
169 	rstrm->sendsize = fix_buf_size(sendsize);
170 	rstrm->recvsize = fix_buf_size(recvsize);
171 	rstrm->out_base = malloc(rstrm->sendsize);
172 	if (rstrm->out_base == NULL) {
173 		(void) syslog(LOG_ERR, mem_err_msg_rec);
174 		free(rstrm);
175 		return;
176 	}
177 	rstrm->in_base = malloc(rstrm->recvsize);
178 	if (rstrm->in_base == NULL) {
179 		(void) syslog(LOG_ERR, mem_err_msg_rec);
180 		free(rstrm->out_base);
181 		free(rstrm);
182 		return;
183 	}
184 
185 	/*
186 	 * now the rest ...
187 	 */
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 	/* LINTED pointer cast */
196 	rstrm->frag_header = (uint32_t *)rstrm->out_base;
197 	rstrm->out_finger += sizeof (uint_t);
198 	rstrm->out_boundry += rstrm->sendsize;
199 	rstrm->frag_sent = FALSE;
200 	rstrm->in_boundry = rstrm->in_base;
201 	rstrm->in_finger = (rstrm->in_boundry += rstrm->recvsize);
202 	rstrm->fbtbc = 0;
203 	rstrm->last_frag = TRUE;
204 	rstrm->firsttime = 0;
205 	rstrm->in_nonblock = 0;
206 	rstrm->in_needpoll = 1;
207 	rstrm->in_maxrecsz = 0;
208 	rstrm->in_nextrec = rstrm->in_base;
209 	rstrm->in_nextrecsz = 0;
210 }
211 
212 /*
213  * Align input stream.  If all applications behaved correctly, this
214  * defensive procedure will not be necessary, since received data will be
215  * aligned correctly.
216  */
217 static void
218 align_instream(RECSTREAM *rstrm)
219 {
220 	int current = rstrm->in_boundry - rstrm->in_finger;
221 
222 	(void) memcpy(rstrm->in_base, rstrm->in_finger, current);
223 	rstrm->in_finger = rstrm->in_base;
224 	rstrm->in_boundry = rstrm->in_finger + current;
225 }
226 
227 /*
228  * The routines defined below are the xdr ops which will go into the
229  * xdr handle filled in by xdrrec_create.
230  */
231 static bool_t
232 xdrrec_getint32(XDR *xdrs, int32_t *ip)
233 {
234 	/* LINTED pointer cast */
235 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
236 	/* LINTED pointer cast */
237 	int32_t *buflp = (int32_t *)(rstrm->in_finger);
238 	int32_t mylong;
239 
240 	/* first try the inline, fast case */
241 	if ((rstrm->fbtbc >= (int)sizeof (int32_t)) &&
242 		((uint_t)(rstrm->in_boundry - (caddr_t)buflp) >=
243 					(uint_t)sizeof (int32_t))) {
244 		/*
245 		 * Check if buflp is longword aligned.  If not, align it.
246 		 */
247 		if (((uintptr_t)buflp) & ((int)sizeof (int32_t) - 1)) {
248 			align_instream(rstrm);
249 			/* LINTED pointer cast */
250 			buflp = (int32_t *)(rstrm->in_finger);
251 		}
252 		*ip = (int32_t)ntohl((uint32_t)(*buflp));
253 		rstrm->fbtbc -= (int)sizeof (int32_t);
254 		rstrm->in_finger += sizeof (int32_t);
255 	} else {
256 		if (!xdrrec_getbytes(xdrs, (caddr_t)&mylong, sizeof (int32_t)))
257 			return (FALSE);
258 		*ip = (int32_t)ntohl((uint32_t)mylong);
259 	}
260 	return (TRUE);
261 }
262 
263 static bool_t
264 xdrrec_putint32(XDR *xdrs, int32_t *ip)
265 {
266 	/* LINTED pointer cast */
267 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
268 	/* LINTED pointer cast */
269 	int32_t *dest_lp = ((int32_t *)(rstrm->out_finger));
270 
271 	if ((rstrm->out_finger += sizeof (int32_t)) > rstrm->out_boundry) {
272 		/*
273 		 * this case should almost never happen so the code is
274 		 * inefficient
275 		 */
276 		rstrm->out_finger -= sizeof (int32_t);
277 		rstrm->frag_sent = TRUE;
278 		if (!flush_out(rstrm, FALSE))
279 			return (FALSE);
280 		/* LINTED pointer cast */
281 		dest_lp = ((int32_t *)(rstrm->out_finger));
282 		rstrm->out_finger += sizeof (int32_t);
283 	}
284 	*dest_lp = (int32_t)htonl((uint32_t)(*ip));
285 	return (TRUE);
286 }
287 
288 static bool_t
289 xdrrec_getlong(XDR *xdrs, long *lp)
290 {
291 	int32_t i;
292 
293 	if (!xdrrec_getint32(xdrs, &i))
294 		return (FALSE);
295 	*lp = (long)i;
296 	return (TRUE);
297 }
298 
299 static bool_t
300 xdrrec_putlong(XDR *xdrs, long *lp)
301 {
302 	int32_t i;
303 
304 #if defined(_LP64)
305 	if ((*lp > INT32_MAX) || (*lp < INT32_MIN))
306 		return (FALSE);
307 #endif
308 
309 	i = (int32_t)*lp;
310 
311 	return (xdrrec_putint32(xdrs, &i));
312 }
313 
314 static bool_t	/* must manage buffers, fragments, and records */
315 xdrrec_getbytes(XDR *xdrs, caddr_t addr, int len)
316 {
317 	/* LINTED pointer cast */
318 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
319 	int current;
320 
321 	while (len > 0) {
322 		current = rstrm->fbtbc;
323 		if (current == 0) {
324 			if (rstrm->last_frag)
325 				return (FALSE);
326 			if (!set_input_fragment(rstrm))
327 				return (FALSE);
328 			continue;
329 		}
330 		current = (len < current) ? len : current;
331 		if (!get_input_bytes(rstrm, addr, current, FALSE))
332 			return (FALSE);
333 		addr += current;
334 		rstrm->fbtbc -= current;
335 		len -= current;
336 	}
337 	return (TRUE);
338 }
339 
340 static bool_t
341 xdrrec_putbytes(XDR *xdrs, caddr_t addr, int len)
342 {
343 	/* LINTED pointer cast */
344 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
345 	int current;
346 
347 	while (len > 0) {
348 
349 		current = (uintptr_t)rstrm->out_boundry -
350 			(uintptr_t)rstrm->out_finger;
351 		current = (len < current) ? len : current;
352 		(void) memcpy(rstrm->out_finger, addr, current);
353 		rstrm->out_finger += current;
354 		addr += current;
355 		len -= current;
356 		if (rstrm->out_finger == rstrm->out_boundry) {
357 			rstrm->frag_sent = TRUE;
358 			if (!flush_out(rstrm, FALSE))
359 				return (FALSE);
360 		}
361 	}
362 	return (TRUE);
363 }
364 /*
365  * This is just like the ops vector x_getbytes(), except that
366  * instead of returning success or failure on getting a certain number
367  * of bytes, it behaves much more like the read() system call against a
368  * pipe -- it returns up to the number of bytes requested and a return of
369  * zero indicates end-of-record.  A -1 means something very bad happened.
370  */
371 uint_t /* must manage buffers, fragments, and records */
372 xdrrec_readbytes(XDR *xdrs, caddr_t addr, uint_t l)
373 {
374 	/* LINTED pointer cast */
375 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
376 	int current, len;
377 
378 	len = l;
379 	while (len > 0) {
380 		current = rstrm->fbtbc;
381 		if (current == 0) {
382 			if (rstrm->last_frag)
383 				return (l - len);
384 			if (!set_input_fragment(rstrm))
385 				return ((uint_t)-1);
386 			continue;
387 		}
388 		current = (len < current) ? len : current;
389 		if (!get_input_bytes(rstrm, addr, current, FALSE))
390 			return ((uint_t)-1);
391 		addr += current;
392 		rstrm->fbtbc -= current;
393 		len -= current;
394 	}
395 	return (l - len);
396 }
397 
398 static uint_t
399 xdrrec_getpos(XDR *xdrs)
400 {
401 	/* LINTED pointer cast */
402 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
403 	int32_t pos;
404 
405 	pos = lseek((intptr_t)rstrm->tcp_handle, 0, 1);
406 	if (pos != -1)
407 		switch (xdrs->x_op) {
408 
409 		case XDR_ENCODE:
410 			pos += rstrm->out_finger - rstrm->out_base;
411 			break;
412 
413 		case XDR_DECODE:
414 			pos -= rstrm->in_boundry - rstrm->in_finger;
415 			break;
416 
417 		default:
418 			pos = (uint_t)-1;
419 			break;
420 		}
421 	return ((uint_t)pos);
422 }
423 
424 static bool_t
425 xdrrec_setpos(XDR *xdrs, uint_t pos)
426 {
427 	/* LINTED pointer cast */
428 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
429 	uint_t currpos = xdrrec_getpos(xdrs);
430 	int delta = currpos - pos;
431 	caddr_t newpos;
432 
433 	if ((int)currpos != -1)
434 		switch (xdrs->x_op) {
435 
436 		case XDR_ENCODE:
437 			newpos = rstrm->out_finger - delta;
438 			if ((newpos > (caddr_t)(rstrm->frag_header)) &&
439 				(newpos < rstrm->out_boundry)) {
440 				rstrm->out_finger = newpos;
441 				return (TRUE);
442 			}
443 			break;
444 
445 		case XDR_DECODE:
446 			newpos = rstrm->in_finger - delta;
447 			if ((delta < (int)(rstrm->fbtbc)) &&
448 				(newpos <= rstrm->in_boundry) &&
449 				(newpos >= rstrm->in_base)) {
450 				rstrm->in_finger = newpos;
451 				rstrm->fbtbc -= delta;
452 				return (TRUE);
453 			}
454 			break;
455 		}
456 	return (FALSE);
457 }
458 
459 static rpc_inline_t *
460 xdrrec_inline(XDR *xdrs, int len)
461 {
462 	/* LINTED pointer cast */
463 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
464 	rpc_inline_t *buf = NULL;
465 
466 	switch (xdrs->x_op) {
467 
468 	case XDR_ENCODE:
469 		if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
470 			/* LINTED pointer cast */
471 			buf = (rpc_inline_t *)rstrm->out_finger;
472 			rstrm->out_finger += len;
473 		}
474 		break;
475 
476 	case XDR_DECODE:
477 		if ((len <= rstrm->fbtbc) &&
478 			((rstrm->in_finger + len) <= rstrm->in_boundry)) {
479 			/*
480 			 * Check if rstrm->in_finger is longword aligned;
481 			 * if not, align it.
482 			 */
483 			if (((intptr_t)rstrm->in_finger) &
484 			    (sizeof (int32_t) - 1))
485 				align_instream(rstrm);
486 			/* LINTED pointer cast */
487 			buf = (rpc_inline_t *)rstrm->in_finger;
488 			rstrm->fbtbc -= len;
489 			rstrm->in_finger += len;
490 		}
491 		break;
492 	}
493 	return (buf);
494 }
495 
496 static void
497 xdrrec_destroy(XDR *xdrs)
498 {
499 	/* LINTED pointer cast */
500 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
501 
502 	free(rstrm->out_base);
503 	free(rstrm->in_base);
504 	free(rstrm);
505 }
506 
507 
508 /*
509  * Exported routines to manage xdr records
510  */
511 
512 /*
513  * Before reading (deserializing) from the stream, one should always call
514  * this procedure to guarantee proper record alignment.
515  */
516 bool_t
517 xdrrec_skiprecord(XDR *xdrs)
518 {
519 	/* LINTED pointer cast */
520 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
521 
522 	if (rstrm->in_nonblock) {
523 		enum xprt_stat pstat;
524 		/*
525 		 * Read and discard a record from the non-blocking
526 		 * buffer. Return succes only if a complete record can
527 		 * be retrieved without blocking, or if the buffer was
528 		 * empty and there was no data to fetch.
529 		 */
530 		if (__xdrrec_getbytes_nonblock(xdrs, &pstat) ||
531 			(pstat == XPRT_MOREREQS &&
532 				rstrm->in_finger == rstrm->in_boundry)) {
533 			rstrm->fbtbc = 0;
534 			return (TRUE);
535 		}
536 		return (FALSE);
537 	}
538 	while (rstrm->fbtbc > 0 || (!rstrm->last_frag)) {
539 		if (!skip_input_bytes(rstrm, rstrm->fbtbc))
540 			return (FALSE);
541 		rstrm->fbtbc = 0;
542 		if ((!rstrm->last_frag) && (!set_input_fragment(rstrm)))
543 			return (FALSE);
544 	}
545 	rstrm->last_frag = FALSE;
546 	return (TRUE);
547 }
548 
549 /*
550  * Look ahead fuction.
551  * Returns TRUE iff there is no more input in the buffer
552  * after consuming the rest of the current record.
553  */
554 bool_t
555 xdrrec_eof(XDR *xdrs)
556 {
557 	/* LINTED pointer cast */
558 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
559 
560 	if (rstrm->in_nonblock) {
561 		/*
562 		 * If in_needpoll is true, the non-blocking XDR stream
563 		 * does not have a complete record.
564 		 */
565 		return (rstrm->in_needpoll);
566 	}
567 	while (rstrm->fbtbc > 0 || (!rstrm->last_frag)) {
568 		if (!skip_input_bytes(rstrm, rstrm->fbtbc))
569 			return (TRUE);
570 		rstrm->fbtbc = 0;
571 		if ((!rstrm->last_frag) && (!set_input_fragment(rstrm)))
572 			return (TRUE);
573 	}
574 	if (rstrm->in_finger == rstrm->in_boundry)
575 		return (TRUE);
576 	return (FALSE);
577 }
578 
579 /*
580  * The client must tell the package when an end-of-record has occurred.
581  * The second parameters tells whether the record should be flushed to the
582  * (output) tcp stream.  (This let's the package support batched or
583  * pipelined procedure calls.)  TRUE => immmediate flush to tcp connection.
584  */
585 bool_t
586 xdrrec_endofrecord(XDR *xdrs, bool_t sendnow)
587 {
588 	/* LINTED pointer cast */
589 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
590 	uint32_t len;	/* fragment length */
591 
592 	if (sendnow || rstrm->frag_sent ||
593 		((uintptr_t)rstrm->out_finger + sizeof (uint32_t) >=
594 		(uintptr_t)rstrm->out_boundry)) {
595 		rstrm->frag_sent = FALSE;
596 		return (flush_out(rstrm, TRUE));
597 	}
598 	len = (uintptr_t)(rstrm->out_finger) - (uintptr_t)(rstrm->frag_header) -
599 		sizeof (uint32_t);
600 	*(rstrm->frag_header) = htonl((uint32_t)len | LAST_FRAG);
601 	/* LINTED pointer cast */
602 	rstrm->frag_header = (uint32_t *)rstrm->out_finger;
603 	rstrm->out_finger += sizeof (uint32_t);
604 	return (TRUE);
605 }
606 
607 
608 /*
609  * Internal useful routines
610  */
611 static bool_t
612 flush_out(RECSTREAM *rstrm, bool_t eor)
613 {
614 	uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
615 	uint32_t len = (uintptr_t)(rstrm->out_finger) -
616 		(uintptr_t)(rstrm->frag_header) - sizeof (uint32_t);
617 	int written;
618 
619 	*(rstrm->frag_header) = htonl(len | eormask);
620 	len = (uintptr_t)(rstrm->out_finger) - (uintptr_t)(rstrm->out_base);
621 
622 	written = (*(rstrm->writeit))
623 	    (rstrm->tcp_handle, rstrm->out_base, (int)len);
624 	/*
625 	 * Handle the specific 'CANT_STORE' error. In this case, the
626 	 * fragment must be cleared.
627 	 */
628 	if ((written != (int)len) && (written != -2))
629 		return (FALSE);
630 	/* LINTED pointer cast */
631 	rstrm->frag_header = (uint32_t *)rstrm->out_base;
632 	rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof (uint32_t);
633 
634 	return (TRUE);
635 }
636 
637 /* knows nothing about records!  Only about input buffers */
638 static bool_t
639 fill_input_buf(RECSTREAM *rstrm, bool_t do_align)
640 {
641 	caddr_t where;
642 	int len;
643 
644 	if (rstrm->in_nonblock) {
645 		/* Should never get here in the non-blocking case */
646 		return (FALSE);
647 	}
648 	where = rstrm->in_base;
649 	if (do_align) {
650 		len = rstrm->recvsize;
651 	} else {
652 		uint_t i = (uintptr_t)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
653 
654 		where += i;
655 		len = rstrm->recvsize - i;
656 	}
657 	if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
658 		return (FALSE);
659 	rstrm->in_finger = where;
660 	where += len;
661 	rstrm->in_boundry = where;
662 	return (TRUE);
663 }
664 
665 /* knows nothing about records!  Only about input buffers */
666 static bool_t
667 get_input_bytes(RECSTREAM *rstrm, caddr_t addr,
668 		int len, bool_t do_align)
669 {
670 	int current;
671 
672 	if (rstrm->in_nonblock) {
673 		/*
674 		 * Data should already be in the rstrm buffer, so we just
675 		 * need to copy it to 'addr'.
676 		 */
677 		current = (int)(rstrm->in_boundry - rstrm->in_finger);
678 		if (len > current)
679 			return (FALSE);
680 		(void) memcpy(addr, rstrm->in_finger, len);
681 		rstrm->in_finger += len;
682 		addr += len;
683 		return (TRUE);
684 	}
685 
686 	while (len > 0) {
687 		current = (intptr_t)rstrm->in_boundry -
688 			(intptr_t)rstrm->in_finger;
689 		if (current == 0) {
690 			if (!fill_input_buf(rstrm, do_align))
691 				return (FALSE);
692 			continue;
693 		}
694 		current = (len < current) ? len : current;
695 		(void) memcpy(addr, rstrm->in_finger, current);
696 		rstrm->in_finger += current;
697 		addr += current;
698 		len -= current;
699 		do_align = FALSE;
700 	}
701 	return (TRUE);
702 }
703 
704 /* next four bytes of the input stream are treated as a header */
705 static bool_t
706 set_input_fragment(RECSTREAM *rstrm)
707 {
708 	uint32_t header;
709 
710 	if (rstrm->in_nonblock) {
711 		/*
712 		 * In the non-blocking case, the fragment headers should
713 		 * already have been consumed, so we should never get
714 		 * here. Might as well return failure right away.
715 		 */
716 		return (FALSE);
717 	}
718 	if (!get_input_bytes(rstrm, (caddr_t)&header, (int)sizeof (header),
719 							rstrm->last_frag))
720 		return (FALSE);
721 	header = (uint32_t)ntohl(header);
722 	rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
723 	rstrm->fbtbc = header & (~LAST_FRAG);
724 	return (TRUE);
725 }
726 
727 /* consumes input bytes; knows nothing about records! */
728 static bool_t
729 skip_input_bytes(RECSTREAM *rstrm, int32_t cnt)
730 {
731 	int current;
732 
733 	while (cnt > 0) {
734 		current = (intptr_t)rstrm->in_boundry -
735 			(intptr_t)rstrm->in_finger;
736 		if (current == 0) {
737 			if (!fill_input_buf(rstrm, FALSE))
738 				return (FALSE);
739 			continue;
740 		}
741 		current = (cnt < current) ? cnt : current;
742 		rstrm->in_finger += current;
743 		cnt -= current;
744 	}
745 	return (TRUE);
746 }
747 
748 
749 static bool_t
750 __xdrrec_nonblock_realloc(RECSTREAM *rstrm, uint32_t newsize)
751 {
752 	caddr_t newbuf = rstrm->in_base;
753 	ptrdiff_t offset;
754 	bool_t ret = TRUE;
755 
756 	if (newsize > rstrm->recvsize) {
757 		newbuf = (caddr_t)realloc(newbuf, newsize);
758 		if (newbuf == 0) {
759 			ret = FALSE;
760 		} else {
761 			/* Make pointers valid for the new buffer */
762 			offset = newbuf - rstrm->in_base;
763 			rstrm->in_finger += offset;
764 			rstrm->in_boundry += offset;
765 			rstrm->in_nextrec += offset;
766 			rstrm->in_base = newbuf;
767 			rstrm->recvsize = newsize;
768 		}
769 	}
770 
771 	return (ret);
772 }
773 
774 /*
775  * adjust sizes and allocate buffer quad byte aligned
776  */
777 bool_t
778 __xdrrec_set_conn_nonblock(XDR *xdrs, uint32_t tcp_maxrecsz)
779 {
780 	/* LINTED pointer cast */
781 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
782 	size_t newsize;
783 
784 	rstrm->in_nonblock = TRUE;
785 	if (tcp_maxrecsz == 0) {
786 		/*
787 		 * If maxrecsz has not been set, use the default
788 		 * that was set from xdrrec_create() and
789 		 * fix_buf_size()
790 		 */
791 		rstrm->in_maxrecsz = rstrm->recvsize;
792 		return (TRUE);
793 	}
794 	rstrm->in_maxrecsz = tcp_maxrecsz;
795 	if (tcp_maxrecsz <= rstrm->recvsize)
796 		return (TRUE);
797 
798 	/*
799 	 * For nonblocked connection, the entire record is read into the
800 	 * buffer before any xdr processing. This implies that the record
801 	 * size must allow for the maximum expected message size of the
802 	 * service. However, it's inconvenient to allocate very large
803 	 * buffers up front, so we limit ourselves to a reasonable
804 	 * default size here, and reallocate (up to the maximum record
805 	 * size allowed for the connection) as necessary.
806 	 */
807 	if ((newsize = tcp_maxrecsz) > RPC_MAXDATASIZE) {
808 		newsize = RPC_MAXDATASIZE;
809 	}
810 	if (!__xdrrec_nonblock_realloc(rstrm, newsize)) {
811 		(void) syslog(LOG_ERR, mem_err_msg_rec);
812 		free(rstrm->out_base);
813 		free(rstrm->in_base);
814 		free(rstrm);
815 		return (FALSE);
816 	}
817 
818 	return (TRUE);
819 }
820 
821 /*
822  * Retrieve input data from the non-blocking connection, increase
823  * the size of the read buffer if necessary, and check that the
824  * record size stays below the allowed maximum for the connection.
825  */
826 bool_t
827 __xdrrec_getbytes_nonblock(XDR *xdrs, enum xprt_stat *pstat)
828 {
829 	/* LINTED pointer cast */
830 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
831 	uint32_t prevbytes_thisrec, minreqrecsize;
832 	uint32_t *header;
833 	int32_t len_received = 0;
834 	uint32_t unprocessed = 0;
835 
836 	/*
837 	 * For connection oriented protocols, there's no guarantee that
838 	 * we will receive the data nicely chopped into records, no
839 	 * matter how it was sent. We use the in_nextrec pointer to
840 	 * indicate where in the buffer the next record starts. If
841 	 * in_nextrec != in_base, there's data in the buffer from
842 	 * previous reads, and if in_nextrecsz > 0, we need to copy
843 	 * the portion of the next record already read to the start of
844 	 * the input buffer
845 	 */
846 	if (rstrm->in_nextrecsz > 0) {
847 		/* Starting on new record with data already in the buffer */
848 		(void) memmove(rstrm->in_base, rstrm->in_nextrec,
849 			rstrm->in_nextrecsz);
850 		rstrm->in_nextrec = rstrm->in_finger = rstrm->in_base;
851 		rstrm->in_boundry = rstrm->in_nextrec + rstrm->in_nextrecsz;
852 		unprocessed = rstrm->in_nextrecsz;
853 		rstrm->in_nextrecsz = 0;
854 	} else if (rstrm->in_nextrec == rstrm->in_base) {
855 		/* Starting on new record with empty buffer */
856 		rstrm->in_boundry = rstrm->in_finger = rstrm->in_base;
857 		rstrm->last_frag = FALSE;
858 		rstrm->in_needpoll = TRUE;
859 	}
860 
861 	prevbytes_thisrec = (uint32_t)(rstrm->in_boundry - rstrm->in_base);
862 
863 	/* Do we need to retrieve data ? */
864 	if (rstrm->in_needpoll) {
865 		int len_requested, len_total_received;
866 
867 		rstrm->in_needpoll = FALSE;
868 		len_total_received =
869 			(int)(rstrm->in_boundry - rstrm->in_base);
870 		len_requested = rstrm->recvsize - len_total_received;
871 		/*
872 		 * if len_requested is 0, this means that the input
873 		 * buffer is full and need to be increased.
874 		 * The minimum record size we will need is whatever's
875 		 * already in the buffer, plus what's yet to be
876 		 * consumed in the current fragment, plus space for at
877 		 * least one more fragment header, if this is not the
878 		 * last fragment. We use the RNDUP() macro to
879 		 * account for possible realignment of the next
880 		 * fragment header.
881 		 */
882 		if (len_requested == 0) {
883 			minreqrecsize = rstrm->recvsize +
884 			    rstrm->fbtbc +
885 			    (rstrm->last_frag ? 0 : sizeof (*header));
886 			minreqrecsize = RNDUP(minreqrecsize);
887 			if (minreqrecsize == rstrm->recvsize) {
888 				/*
889 				 * no more bytes to be consumed and
890 				 * last fragment. We should never end up
891 				 * here. Might as well return failure
892 				 * right away.
893 				 */
894 				*pstat = XPRT_DIED;
895 				return (FALSE);
896 			}
897 			if (minreqrecsize > rstrm->in_maxrecsz)
898 				goto recsz_invalid;
899 			else
900 				goto needpoll;
901 		}
902 		if ((len_received = (*(rstrm->readit))(rstrm->tcp_handle,
903 				rstrm->in_boundry, len_requested)) == -1) {
904 			*pstat = XPRT_DIED;
905 			return (FALSE);
906 		}
907 		rstrm->in_boundry += len_received;
908 		rstrm->in_nextrec = rstrm->in_boundry;
909 	}
910 
911 	/* Account for any left over data from previous processing */
912 	len_received += unprocessed;
913 
914 	/* Set a lower limit on the buffer space we'll need */
915 	minreqrecsize = prevbytes_thisrec + rstrm->fbtbc;
916 
917 	/*
918 	 * Consume bytes for this record until it's either complete,
919 	 * rejected, or we need to poll for more bytes.
920 	 *
921 	 * If fbtbc == 0, in_finger points to the start of the fragment
922 	 * header. Otherwise, it points to the start of the fragment data.
923 	 */
924 	while (len_received > 0) {
925 		if (rstrm->fbtbc == 0) {
926 			uint32_t hdrlen, minfraglen = 0;
927 			uint32_t len_recvd_thisfrag;
928 			bool_t last_frag;
929 
930 			len_recvd_thisfrag = (uint32_t)(rstrm->in_boundry -
931 						rstrm->in_finger);
932 			/* LINTED pointer cast */
933 			header = (uint32_t *)rstrm->in_finger;
934 			hdrlen = (len_recvd_thisfrag < sizeof (*header)) ?
935 				len_recvd_thisfrag : sizeof (*header);
936 			(void) memcpy(&minfraglen, header, hdrlen);
937 			last_frag = (ntohl(minfraglen) & LAST_FRAG) != 0;
938 			minfraglen = ntohl(minfraglen) & (~LAST_FRAG);
939 			/*
940 			 * The minimum record size we will need is whatever's
941 			 * already in the buffer, plus the size of this
942 			 * fragment, plus (if this isn't the last fragment)
943 			 * space for at least one more fragment header. We
944 			 * use the RNDUP() macro to account for possible
945 			 * realignment of the next fragment header.
946 			 */
947 			minreqrecsize += minfraglen +
948 					(last_frag?0:sizeof (*header));
949 			minreqrecsize = RNDUP(minreqrecsize);
950 
951 			if (hdrlen < sizeof (*header)) {
952 				/*
953 				 * We only have a partial fragment header,
954 				 * but we can still put a lower limit on the
955 				 * final fragment size, and check against the
956 				 * maximum allowed.
957 				 */
958 				if (len_recvd_thisfrag > 0 &&
959 					(minreqrecsize > rstrm->in_maxrecsz)) {
960 					goto recsz_invalid;
961 				}
962 				/* Need more bytes to obtain fbtbc value */
963 				goto needpoll;
964 			}
965 			/*
966 			 * We've got a complete fragment header, so
967 			 * 'minfraglen' is the actual fragment length, and
968 			 * 'minreqrecsize' the requested record size.
969 			 */
970 			rstrm->last_frag = last_frag;
971 			rstrm->fbtbc = minfraglen;
972 			/*
973 			 * Check that the sum of the total number of bytes read
974 			 * so far (for the record) and the size of the incoming
975 			 * fragment is less than the maximum allowed.
976 			 *
977 			 * If this is the last fragment, also check that the
978 			 * record (message) meets the minimum length
979 			 * requirement.
980 			 *
981 			 * If this isn't the last fragment, check for a zero
982 			 * fragment length. Accepting such fragments would
983 			 * leave us open to an attack where the sender keeps
984 			 * the connection open indefinitely, without any
985 			 * progress, by occasionally sending a zero length
986 			 * fragment.
987 			 */
988 			if ((minreqrecsize > rstrm->in_maxrecsz) ||
989 			(rstrm->last_frag && minreqrecsize < MIN_FRAG) ||
990 			(!rstrm->last_frag && minfraglen == 0)) {
991 recsz_invalid:
992 				rstrm->fbtbc = 0;
993 				rstrm->last_frag = 1;
994 				*pstat = XPRT_DIED;
995 				return (FALSE);
996 			}
997 			/*
998 			 * Make this fragment abut the previous one. If it's
999 			 * the first fragment, just advance in_finger past
1000 			 * the header. This avoids buffer copying for the
1001 			 * usual case where there's one fragment per record.
1002 			 */
1003 			if (rstrm->in_finger == rstrm->in_base) {
1004 				rstrm->in_finger += sizeof (*header);
1005 			} else {
1006 				rstrm->in_boundry -= sizeof (*header);
1007 				(void) memmove(rstrm->in_finger,
1008 					rstrm->in_finger + sizeof (*header),
1009 					rstrm->in_boundry - rstrm->in_finger);
1010 			}
1011 			/* Consume the fragment header */
1012 			if (len_received > sizeof (*header)) {
1013 				len_received -= sizeof (*header);
1014 			} else {
1015 				len_received = 0;
1016 			}
1017 		}
1018 		/*
1019 		 * Consume whatever fragment bytes we have.
1020 		 * If we've received all bytes for this fragment, advance
1021 		 * in_finger to point to the start of the next fragment
1022 		 * header. Otherwise, make fbtbc tell how much is left in
1023 		 * in this fragment and advance finger to point to end of
1024 		 * fragment data.
1025 		 */
1026 		if (len_received >= rstrm->fbtbc) {
1027 			len_received -= rstrm->fbtbc;
1028 			rstrm->in_finger += rstrm->fbtbc;
1029 			rstrm->fbtbc = 0;
1030 		} else {
1031 			rstrm->fbtbc -= len_received;
1032 			rstrm->in_finger += len_received;
1033 			len_received = 0;
1034 		}
1035 		/*
1036 		 * If there's more data in the buffer, there are two
1037 		 * possibilities:
1038 		 *
1039 		 * (1)	This is the last fragment, so the extra data
1040 		 *	presumably belongs to the next record.
1041 		 *
1042 		 * (2)	Not the last fragment, so we'll start over
1043 		 *	from the top of the loop.
1044 		 */
1045 		if (len_received > 0 && rstrm->last_frag) {
1046 			rstrm->in_nextrec = rstrm->in_finger;
1047 			rstrm->in_nextrecsz = (uint32_t)(rstrm->in_boundry -
1048 							rstrm->in_nextrec);
1049 			len_received = 0;
1050 		}
1051 	}
1052 
1053 	/* Was this the last fragment, and have we read the entire record ? */
1054 	if (rstrm->last_frag && rstrm->fbtbc == 0) {
1055 		*pstat = XPRT_MOREREQS;
1056 		/*
1057 		 * We've been using both in_finger and fbtbc for our own
1058 		 * purposes. Now's the time to update them to be what
1059 		 * xdrrec_inline() expects. Set in_finger to point to the
1060 		 * start of data for this record, and fbtbc to the number
1061 		 * of bytes in the record.
1062 		 */
1063 		rstrm->fbtbc = (int)(rstrm->in_finger -
1064 				rstrm->in_base - sizeof (*header));
1065 		rstrm->in_finger = rstrm->in_base + sizeof (*header);
1066 		if (rstrm->in_nextrecsz == 0)
1067 			rstrm->in_nextrec = rstrm->in_base;
1068 		return (TRUE);
1069 	}
1070 needpoll:
1071 	/*
1072 	 * Need more bytes, so we set the needpoll flag, and go back to
1073 	 * the main RPC request loop. However, first we reallocate the
1074 	 * input buffer, if necessary.
1075 	 */
1076 	if (minreqrecsize > rstrm->recvsize) {
1077 		if (!__xdrrec_nonblock_realloc(rstrm, minreqrecsize)) {
1078 			rstrm->fbtbc = 0;
1079 			rstrm->last_frag = 1;
1080 			*pstat = XPRT_DIED;
1081 			return (FALSE);
1082 		}
1083 	}
1084 
1085 	rstrm->in_needpoll = TRUE;
1086 	*pstat = XPRT_MOREREQS;
1087 	return (FALSE);
1088 }
1089 
1090 int
1091 __is_xdrrec_first(XDR *xdrs)
1092 {
1093 	/* LINTED pointer cast */
1094 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
1095 	return ((rstrm->firsttime == TRUE) ? 1 : 0);
1096 }
1097 
1098 int
1099 __xdrrec_setfirst(XDR *xdrs)
1100 {
1101 	/* LINTED pointer cast */
1102 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
1103 
1104 	/*
1105 	 * Set rstrm->firsttime only if the input buffer is empty.
1106 	 * Otherwise, the first read from the network could skip
1107 	 * a poll.
1108 	 */
1109 	if (rstrm->in_finger == rstrm->in_boundry)
1110 		rstrm->firsttime = TRUE;
1111 	else
1112 		rstrm->firsttime = FALSE;
1113 	return (1);
1114 }
1115 
1116 int
1117 __xdrrec_resetfirst(XDR *xdrs)
1118 {
1119 	/* LINTED pointer cast */
1120 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
1121 
1122 	rstrm->firsttime = FALSE;
1123 	return (1);
1124 }
1125 
1126 
1127 static uint_t
1128 fix_buf_size(uint_t s)
1129 {
1130 	if (s < 100)
1131 		s = 4000;
1132 	return (RNDUP(s));
1133 }
1134 
1135 
1136 
1137 static bool_t
1138 xdrrec_control(XDR *xdrs, int request, void *info)
1139 {
1140 	/* LINTED pointer cast */
1141 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
1142 	xdr_bytesrec *xptr;
1143 
1144 	switch (request) {
1145 
1146 	case XDR_GET_BYTES_AVAIL:
1147 		/* Check if at end of fragment and not last fragment */
1148 		if ((rstrm->fbtbc == 0)	&& (!rstrm->last_frag))
1149 			if (!set_input_fragment(rstrm)) {
1150 				return (FALSE);
1151 			};
1152 
1153 		xptr = (xdr_bytesrec *)info;
1154 		xptr->xc_is_last_record = rstrm->last_frag;
1155 		xptr->xc_num_avail = rstrm->fbtbc;
1156 
1157 		return (TRUE);
1158 	default:
1159 		return (FALSE);
1160 
1161 	}
1162 
1163 }
1164 
1165 static struct xdr_ops *
1166 xdrrec_ops(void)
1167 {
1168 	static struct xdr_ops ops;
1169 	extern mutex_t	ops_lock;
1170 
1171 /* VARIABLES PROTECTED BY ops_lock: ops */
1172 
1173 	(void) mutex_lock(&ops_lock);
1174 	if (ops.x_getlong == NULL) {
1175 		ops.x_getlong = xdrrec_getlong;
1176 		ops.x_putlong = xdrrec_putlong;
1177 		ops.x_getbytes = xdrrec_getbytes;
1178 		ops.x_putbytes = xdrrec_putbytes;
1179 		ops.x_getpostn = xdrrec_getpos;
1180 		ops.x_setpostn = xdrrec_setpos;
1181 		ops.x_inline = xdrrec_inline;
1182 		ops.x_destroy = xdrrec_destroy;
1183 		ops.x_control = xdrrec_control;
1184 #if defined(_LP64)
1185 		ops.x_getint32 = xdrrec_getint32;
1186 		ops.x_putint32 = xdrrec_putint32;
1187 #endif
1188 	}
1189 	(void) mutex_unlock(&ops_lock);
1190 	return (&ops);
1191 }
1192