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