xref: /titanic_52/usr/src/lib/libnsl/rpc/svc_vc.c (revision c5805b0b8f18ce47991b05b08dc6bedc8a549f49)
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 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
30 /*
31  * Portions of this source code were derived from Berkeley
32  * 4.3 BSD under license from the Regents of the University of
33  * California.
34  */
35 
36 #pragma ident	"%Z%%M%	%I%	%E% SMI"
37 
38 /*
39  * Server side for Connection Oriented RPC.
40  *
41  * Actually implements two flavors of transporter -
42  * a rendezvouser (a listener and connection establisher)
43  * and a record stream.
44  */
45 
46 #include "mt.h"
47 #include "rpc_mt.h"
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <rpc/rpc.h>
51 #include <sys/types.h>
52 #include <errno.h>
53 #include <sys/stat.h>
54 #include <sys/mkdev.h>
55 #include <sys/poll.h>
56 #include <syslog.h>
57 #include <rpc/nettype.h>
58 #include <tiuser.h>
59 #include <string.h>
60 #include <stropts.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <sys/timod.h>
64 #include <limits.h>
65 
66 #ifndef MIN
67 #define	MIN(a, b)	(((a) < (b)) ? (a) : (b))
68 #endif
69 
70 #define	CLEANUP_SIZE	1024
71 
72 extern int nsvc_xdrs;
73 extern int __rpc_connmaxrec;
74 extern int __rpc_irtimeout;
75 
76 extern SVCXPRT	**svc_xports;
77 extern int	__td_setnodelay(int);
78 extern bool_t	__xdrrec_getbytes_nonblock(XDR *, enum xprt_stat *);
79 extern bool_t	__xdrrec_set_conn_nonblock(XDR *, uint32_t);
80 extern int	_t_do_ioctl(int, char *, int, int, int *);
81 extern int	__rpc_legal_connmaxrec(int);
82 /* Structure used to initialize SVC_XP_AUTH(xprt).svc_ah_ops. */
83 extern struct svc_auth_ops svc_auth_any_ops;
84 extern void	__xprt_unregister_private(const SVCXPRT *, bool_t);
85 
86 static struct xp_ops 	*svc_vc_ops(void);
87 static struct xp_ops 	*svc_vc_rendezvous_ops(void);
88 static void		svc_vc_destroy(SVCXPRT *);
89 static bool_t		svc_vc_nonblock(SVCXPRT *, SVCXPRT *);
90 static int		read_vc(SVCXPRT *, caddr_t, int);
91 static int		write_vc(SVCXPRT *, caddr_t, int);
92 static SVCXPRT		*makefd_xprt(int, uint_t, uint_t, t_scalar_t, char *);
93 static bool_t		fd_is_dead(int);
94 static void		update_nonblock_timestamps(SVCXPRT *);
95 
96 struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */
97 	uint_t sendsize;
98 	uint_t recvsize;
99 	struct t_call *t_call;
100 	struct t_bind *t_bind;
101 	t_scalar_t cf_tsdu;
102 	char *cf_cache;
103 	int tcp_flag;
104 	int tcp_keepalive;
105 	int cf_connmaxrec;
106 };
107 
108 struct cf_conn {	/* kept in xprt->xp_p1 for actual connection */
109 	uint_t sendsize;
110 	uint_t recvsize;
111 	enum xprt_stat strm_stat;
112 	uint32_t x_id;
113 	t_scalar_t cf_tsdu;
114 	XDR xdrs;
115 	char *cf_cache;
116 	char verf_body[MAX_AUTH_BYTES];
117 	bool_t cf_conn_nonblock;
118 	time_t cf_conn_nonblock_timestamp;
119 };
120 
121 static int t_rcvall(int, char *, int);
122 static int t_rcvnonblock(SVCXPRT *, caddr_t, int);
123 static void svc_timeout_nonblock_xprt_and_LRU(bool_t);
124 
125 extern int __xdrrec_setfirst(XDR *);
126 extern int __xdrrec_resetfirst(XDR *);
127 extern int __is_xdrrec_first(XDR *);
128 
129 void __svc_nisplus_enable_timestamps(void);
130 void __svc_timeout_nonblock_xprt(void);
131 
132 /*
133  * This is intended as a performance improvement on the old string handling
134  * stuff by read only moving data into the  text segment.
135  * Format = <routine> : <error>
136  */
137 
138 static const char errstring[] = " %s : %s";
139 
140 /* Routine names */
141 
142 static const char svc_vc_create_str[] = "svc_vc_create";
143 static const char svc_fd_create_str[] = "svc_fd_create";
144 static const char makefd_xprt_str[] = "svc_vc_create: makefd_xprt ";
145 static const char rendezvous_request_str[] = "rendezvous_request";
146 static const char svc_vc_fderr[] =
147 		"fd > FD_SETSIZE; Use rpc_control(RPC_SVC_USE_POLLFD,...);";
148 static const char do_accept_str[] = "do_accept";
149 
150 /* error messages */
151 
152 static const char no_mem_str[] = "out of memory";
153 static const char no_tinfo_str[] = "could not get transport information";
154 static const char no_fcntl_getfl_str[] = "could not get status flags and modes";
155 static const char no_nonblock_str[] = "could not set transport non-blocking";
156 
157 /*
158  *  Records a timestamp when data comes in on a descriptor.  This is
159  *  only used if timestamps are enabled with __svc_nisplus_enable_timestamps().
160  */
161 static long *timestamps;
162 static int ntimestamps; /* keep track how many timestamps */
163 static mutex_t timestamp_lock = DEFAULTMUTEX;
164 
165 /*
166  * Used to determine whether the time-out logic should be executed.
167  */
168 static bool_t check_nonblock_timestamps = FALSE;
169 
170 void
171 svc_vc_xprtfree(SVCXPRT *xprt)
172 {
173 /* LINTED pointer alignment */
174 	SVCXPRT_EXT		*xt = xprt ? SVCEXT(xprt) : NULL;
175 	struct cf_rendezvous	*r = xprt ?
176 /* LINTED pointer alignment */
177 				    (struct cf_rendezvous *)xprt->xp_p1 : NULL;
178 
179 	if (!xprt)
180 		return;
181 
182 	if (xprt->xp_tp)
183 		free(xprt->xp_tp);
184 	if (xprt->xp_netid)
185 		free(xprt->xp_netid);
186 	if (xt && (xt->parent == NULL)) {
187 		if (xprt->xp_ltaddr.buf)
188 			free(xprt->xp_ltaddr.buf);
189 		if (xprt->xp_rtaddr.buf)
190 			free(xprt->xp_rtaddr.buf);
191 	}
192 	if (r) {
193 		if (r->t_call)
194 			(void) t_free((char *)r->t_call, T_CALL);
195 		if (r->t_bind)
196 			(void) t_free((char *)r->t_bind, T_BIND);
197 		free(r);
198 	}
199 	svc_xprt_free(xprt);
200 }
201 
202 /*
203  * Usage:
204  *	xprt = svc_vc_create(fd, sendsize, recvsize);
205  * Since connection streams do buffered io similar to stdio, the caller
206  * can specify how big the send and receive buffers are. If recvsize
207  * or sendsize are 0, defaults will be chosen.
208  * fd should be open and bound.
209  */
210 SVCXPRT *
211 svc_vc_create_private(int fd, uint_t sendsize, uint_t recvsize)
212 {
213 	struct cf_rendezvous *r;
214 	SVCXPRT *xprt;
215 	struct t_info tinfo;
216 
217 	if (RPC_FD_NOTIN_FDSET(fd)) {
218 		errno = EBADF;
219 		t_errno = TBADF;
220 		(void) syslog(LOG_ERR, errstring, svc_vc_create_str,
221 		    svc_vc_fderr);
222 		return (NULL);
223 	}
224 	if ((xprt = svc_xprt_alloc()) == NULL) {
225 		(void) syslog(LOG_ERR, errstring,
226 		    svc_vc_create_str, no_mem_str);
227 		return (NULL);
228 	}
229 /* LINTED pointer alignment */
230 	svc_flags(xprt) |= SVC_RENDEZVOUS;
231 
232 	r = calloc(1, sizeof (*r));
233 	if (r == NULL) {
234 		(void) syslog(LOG_ERR, errstring,
235 			svc_vc_create_str, no_mem_str);
236 		svc_vc_xprtfree(xprt);
237 		return (NULL);
238 	}
239 	if (t_getinfo(fd, &tinfo) == -1) {
240 		char errorstr[100];
241 
242 		__tli_sys_strerror(errorstr, sizeof (errorstr),
243 				t_errno, errno);
244 		(void) syslog(LOG_ERR, "%s : %s : %s",
245 			svc_vc_create_str, no_tinfo_str, errorstr);
246 		free(r);
247 		svc_vc_xprtfree(xprt);
248 		return (NULL);
249 	}
250 	/*
251 	 * Find the receive and the send size
252 	 */
253 	r->sendsize = __rpc_get_t_size((int)sendsize, tinfo.tsdu);
254 	r->recvsize = __rpc_get_t_size((int)recvsize, tinfo.tsdu);
255 	if ((r->sendsize == 0) || (r->recvsize == 0)) {
256 		syslog(LOG_ERR,
257 		    "svc_vc_create:  transport does not support "
258 		    "data transfer");
259 		free(r);
260 		svc_vc_xprtfree(xprt);
261 		return (NULL);
262 	}
263 
264 /* LINTED pointer alignment */
265 	r->t_call = (struct t_call *)t_alloc(fd, T_CALL, T_ADDR | T_OPT);
266 	if (r->t_call == NULL) {
267 		(void) syslog(LOG_ERR, errstring,
268 			svc_vc_create_str, no_mem_str);
269 		free(r);
270 		svc_vc_xprtfree(xprt);
271 		return (NULL);
272 	}
273 
274 /* LINTED pointer alignment */
275 	r->t_bind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
276 	if (r->t_bind == NULL) {
277 		(void) syslog(LOG_ERR, errstring,
278 			svc_vc_create_str, no_mem_str);
279 		(void) t_free((char *)r->t_call, T_CALL);
280 		free(r);
281 		svc_vc_xprtfree(xprt);
282 		return (NULL);
283 	}
284 
285 	r->cf_tsdu = tinfo.tsdu;
286 	r->tcp_flag = FALSE;
287 	r->tcp_keepalive = FALSE;
288 	r->cf_connmaxrec = __rpc_connmaxrec;
289 	xprt->xp_fd = fd;
290 	xprt->xp_p1 = (caddr_t)r;
291 	xprt->xp_p2 = NULL;
292 	xprt->xp_verf = _null_auth;
293 	xprt->xp_ops = svc_vc_rendezvous_ops();
294 /* LINTED pointer alignment */
295 	SVC_XP_AUTH(xprt).svc_ah_ops = svc_auth_any_ops;
296 /* LINTED pointer alignment */
297 	SVC_XP_AUTH(xprt).svc_ah_private = NULL;
298 
299 	return (xprt);
300 }
301 
302 SVCXPRT *
303 svc_vc_create(const int fd, const uint_t sendsize, const uint_t recvsize)
304 {
305 	SVCXPRT *xprt;
306 
307 	if ((xprt = svc_vc_create_private(fd, sendsize, recvsize)) != NULL)
308 		xprt_register(xprt);
309 	return (xprt);
310 }
311 
312 SVCXPRT *
313 svc_vc_xprtcopy(SVCXPRT *parent)
314 {
315 	SVCXPRT			*xprt;
316 	struct cf_rendezvous	*r, *pr;
317 	int			fd = parent->xp_fd;
318 
319 	if ((xprt = svc_xprt_alloc()) == NULL)
320 		return (NULL);
321 
322 /* LINTED pointer alignment */
323 	SVCEXT(xprt)->parent = parent;
324 /* LINTED pointer alignment */
325 	SVCEXT(xprt)->flags = SVCEXT(parent)->flags;
326 
327 	xprt->xp_fd = fd;
328 	xprt->xp_ops = svc_vc_rendezvous_ops();
329 	if (parent->xp_tp) {
330 		xprt->xp_tp = (char *)strdup(parent->xp_tp);
331 		if (xprt->xp_tp == NULL) {
332 			syslog(LOG_ERR, "svc_vc_xprtcopy: strdup failed");
333 			svc_vc_xprtfree(xprt);
334 			return (NULL);
335 		}
336 	}
337 	if (parent->xp_netid) {
338 		xprt->xp_netid = (char *)strdup(parent->xp_netid);
339 		if (xprt->xp_netid == NULL) {
340 			syslog(LOG_ERR, "svc_vc_xprtcopy: strdup failed");
341 			if (xprt->xp_tp)
342 				free(xprt->xp_tp);
343 			svc_vc_xprtfree(xprt);
344 			return (NULL);
345 		}
346 	}
347 
348 	/*
349 	 * can share both local and remote address
350 	 */
351 	xprt->xp_ltaddr = parent->xp_ltaddr;
352 	xprt->xp_rtaddr = parent->xp_rtaddr; /* XXX - not used for rendezvous */
353 	xprt->xp_type = parent->xp_type;
354 	xprt->xp_verf = parent->xp_verf;
355 
356 	if ((r = calloc(1, sizeof (*r))) == NULL) {
357 		svc_vc_xprtfree(xprt);
358 		return (NULL);
359 	}
360 	xprt->xp_p1 = (caddr_t)r;
361 /* LINTED pointer alignment */
362 	pr = (struct cf_rendezvous *)parent->xp_p1;
363 	r->sendsize = pr->sendsize;
364 	r->recvsize = pr->recvsize;
365 	r->cf_tsdu = pr->cf_tsdu;
366 	r->cf_cache = pr->cf_cache;
367 	r->tcp_flag = pr->tcp_flag;
368 	r->tcp_keepalive = pr->tcp_keepalive;
369 	r->cf_connmaxrec = pr->cf_connmaxrec;
370 /* LINTED pointer alignment */
371 	r->t_call = (struct t_call *)t_alloc(fd, T_CALL, T_ADDR | T_OPT);
372 	if (r->t_call == NULL) {
373 		svc_vc_xprtfree(xprt);
374 		return (NULL);
375 	}
376 /* LINTED pointer alignment */
377 	r->t_bind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
378 	if (r->t_bind == NULL) {
379 		svc_vc_xprtfree(xprt);
380 		return (NULL);
381 	}
382 
383 	return (xprt);
384 }
385 
386 /*
387  * XXX : Used for setting flag to indicate that this is TCP
388  */
389 
390 /*ARGSUSED*/
391 int
392 __svc_vc_setflag(SVCXPRT *xprt, int flag)
393 {
394 	struct cf_rendezvous *r;
395 
396 /* LINTED pointer alignment */
397 	r = (struct cf_rendezvous *)xprt->xp_p1;
398 	r->tcp_flag = TRUE;
399 	return (1);
400 }
401 
402 /*
403  * used for the actual connection.
404  */
405 SVCXPRT *
406 svc_fd_create_private(int fd, uint_t sendsize, uint_t recvsize)
407 {
408 	struct t_info tinfo;
409 	SVCXPRT *dummy;
410 	struct netbuf tres = {0};
411 
412 	if (RPC_FD_NOTIN_FDSET(fd)) {
413 		errno = EBADF;
414 		t_errno = TBADF;
415 		(void) syslog(LOG_ERR, errstring,
416 		    svc_fd_create_str, svc_vc_fderr);
417 		return (NULL);
418 	}
419 	if (t_getinfo(fd, &tinfo) == -1) {
420 		char errorstr[100];
421 
422 		__tli_sys_strerror(errorstr, sizeof (errorstr),
423 				t_errno, errno);
424 		(void) syslog(LOG_ERR, "%s : %s : %s",
425 			svc_fd_create_str, no_tinfo_str, errorstr);
426 		return (NULL);
427 	}
428 	/*
429 	 * Find the receive and the send size
430 	 */
431 	sendsize = __rpc_get_t_size((int)sendsize, tinfo.tsdu);
432 	recvsize = __rpc_get_t_size((int)recvsize, tinfo.tsdu);
433 	if ((sendsize == 0) || (recvsize == 0)) {
434 		syslog(LOG_ERR, errstring, svc_fd_create_str,
435 			"transport does not support data transfer");
436 		return (NULL);
437 	}
438 	dummy = makefd_xprt(fd, sendsize, recvsize, tinfo.tsdu, NULL);
439 				/* NULL signifies no dup cache */
440 	/* Assign the local bind address */
441 	if (t_getname(fd, &tres, LOCALNAME) == -1)
442 		tres.len = 0;
443 	dummy->xp_ltaddr = tres;
444 	/* Fill in type of service */
445 	dummy->xp_type = tinfo.servtype;
446 	return (dummy);
447 }
448 
449 SVCXPRT *
450 svc_fd_create(const int fd, const uint_t sendsize, const uint_t recvsize)
451 {
452 	SVCXPRT *xprt;
453 
454 	if ((xprt = svc_fd_create_private(fd, sendsize, recvsize)) != NULL)
455 		xprt_register(xprt);
456 	return (xprt);
457 }
458 
459 void
460 svc_fd_xprtfree(SVCXPRT *xprt)
461 {
462 /* LINTED pointer alignment */
463 	SVCXPRT_EXT	*xt = xprt ? SVCEXT(xprt) : NULL;
464 /* LINTED pointer alignment */
465 	struct cf_conn	*cd = xprt ? (struct cf_conn *)xprt->xp_p1 : NULL;
466 
467 	if (!xprt)
468 		return;
469 
470 	if (xprt->xp_tp)
471 		free(xprt->xp_tp);
472 	if (xprt->xp_netid)
473 		free(xprt->xp_netid);
474 	if (xt && (xt->parent == NULL)) {
475 		if (xprt->xp_ltaddr.buf)
476 			free(xprt->xp_ltaddr.buf);
477 		if (xprt->xp_rtaddr.buf)
478 			free(xprt->xp_rtaddr.buf);
479 	}
480 	if (cd) {
481 		XDR_DESTROY(&(cd->xdrs));
482 		free(cd);
483 	}
484 	if (xt && (xt->parent == NULL) && xprt->xp_p2) {
485 /* LINTED pointer alignment */
486 		free(((struct netbuf *)xprt->xp_p2)->buf);
487 		free(xprt->xp_p2);
488 	}
489 	svc_xprt_free(xprt);
490 }
491 
492 static SVCXPRT *
493 makefd_xprt(int fd, uint_t sendsize, uint_t recvsize, t_scalar_t tsdu,
494     char *cache)
495 {
496 	SVCXPRT *xprt;
497 	struct cf_conn *cd;
498 
499 	xprt = svc_xprt_alloc();
500 	if (xprt == NULL) {
501 		(void) syslog(LOG_ERR, errstring, makefd_xprt_str, no_mem_str);
502 		return (NULL);
503 	}
504 /* LINTED pointer alignment */
505 	svc_flags(xprt) |= SVC_CONNECTION;
506 
507 	cd = malloc(sizeof (struct cf_conn));
508 	if (cd == NULL) {
509 		(void) syslog(LOG_ERR, errstring, makefd_xprt_str, no_mem_str);
510 		svc_fd_xprtfree(xprt);
511 		return (NULL);
512 	}
513 	cd->sendsize = sendsize;
514 	cd->recvsize = recvsize;
515 	cd->strm_stat = XPRT_IDLE;
516 	cd->cf_tsdu = tsdu;
517 	cd->cf_cache = cache;
518 	cd->cf_conn_nonblock = FALSE;
519 	cd->cf_conn_nonblock_timestamp = 0;
520 	cd->xdrs.x_ops = NULL;
521 	xdrrec_create(&(cd->xdrs), sendsize, 0, (caddr_t)xprt,
522 			(int(*)())NULL, (int(*)(void *, char *, int))write_vc);
523 	if (cd->xdrs.x_ops == NULL) {
524 		(void) syslog(LOG_ERR, errstring, makefd_xprt_str, no_mem_str);
525 		free(cd);
526 		svc_fd_xprtfree(xprt);
527 		return (NULL);
528 	}
529 
530 	(void) rw_wrlock(&svc_fd_lock);
531 	if (svc_xdrs == NULL) {
532 		svc_xdrs = calloc(FD_INCREMENT,  sizeof (XDR *));
533 		if (svc_xdrs == NULL) {
534 			(void) syslog(LOG_ERR, errstring, makefd_xprt_str,
535 								no_mem_str);
536 			XDR_DESTROY(&(cd->xdrs));
537 			free(cd);
538 			svc_fd_xprtfree(xprt);
539 			(void) rw_unlock(&svc_fd_lock);
540 			return (NULL);
541 		}
542 		nsvc_xdrs = FD_INCREMENT;
543 	}
544 
545 	while (fd >= nsvc_xdrs) {
546 		XDR **tmp_xdrs = svc_xdrs;
547 		tmp_xdrs = realloc(svc_xdrs,
548 				sizeof (XDR *) * (nsvc_xdrs + FD_INCREMENT));
549 		if (tmp_xdrs == NULL) {
550 			(void) syslog(LOG_ERR, errstring, makefd_xprt_str,
551 								no_mem_str);
552 			XDR_DESTROY(&(cd->xdrs));
553 			free(cd);
554 			svc_fd_xprtfree(xprt);
555 			(void) rw_unlock(&svc_fd_lock);
556 			return (NULL);
557 		}
558 
559 		svc_xdrs = tmp_xdrs;
560 		/* initial the new array to 0 from the last allocated array */
561 		(void) memset(&svc_xdrs[nsvc_xdrs], 0,
562 					sizeof (XDR *) * FD_INCREMENT);
563 		nsvc_xdrs += FD_INCREMENT;
564 	}
565 
566 	if (svc_xdrs[fd] != NULL) {
567 		XDR_DESTROY(svc_xdrs[fd]);
568 	} else if ((svc_xdrs[fd] = malloc(sizeof (XDR))) == NULL) {
569 		(void) syslog(LOG_ERR, errstring, makefd_xprt_str, no_mem_str);
570 		XDR_DESTROY(&(cd->xdrs));
571 		free(cd);
572 		svc_fd_xprtfree(xprt);
573 		(void) rw_unlock(&svc_fd_lock);
574 		return (NULL);
575 	}
576 	(void) memset(svc_xdrs[fd], 0, sizeof (XDR));
577 	xdrrec_create(svc_xdrs[fd], 0, recvsize, (caddr_t)xprt,
578 			(int(*)(void *, char *, int))read_vc, (int(*)())NULL);
579 	if (svc_xdrs[fd]->x_ops == NULL) {
580 		free(svc_xdrs[fd]);
581 		svc_xdrs[fd] = NULL;
582 		XDR_DESTROY(&(cd->xdrs));
583 		free(cd);
584 		svc_fd_xprtfree(xprt);
585 		(void) rw_unlock(&svc_fd_lock);
586 		return (NULL);
587 	}
588 	(void) rw_unlock(&svc_fd_lock);
589 
590 	xprt->xp_p1 = (caddr_t)cd;
591 	xprt->xp_p2 = NULL;
592 	xprt->xp_verf.oa_base = cd->verf_body;
593 	xprt->xp_ops = svc_vc_ops();	/* truely deals with calls */
594 	xprt->xp_fd = fd;
595 	return (xprt);
596 }
597 
598 SVCXPRT *
599 svc_fd_xprtcopy(SVCXPRT *parent)
600 {
601 	SVCXPRT			*xprt;
602 	struct cf_conn		*cd, *pcd;
603 
604 	if ((xprt = svc_xprt_alloc()) == NULL)
605 		return (NULL);
606 
607 /* LINTED pointer alignment */
608 	SVCEXT(xprt)->parent = parent;
609 /* LINTED pointer alignment */
610 	SVCEXT(xprt)->flags = SVCEXT(parent)->flags;
611 
612 	xprt->xp_fd = parent->xp_fd;
613 	xprt->xp_ops = svc_vc_ops();
614 	if (parent->xp_tp) {
615 		xprt->xp_tp = (char *)strdup(parent->xp_tp);
616 		if (xprt->xp_tp == NULL) {
617 			syslog(LOG_ERR, "svc_fd_xprtcopy: strdup failed");
618 			svc_fd_xprtfree(xprt);
619 			return (NULL);
620 		}
621 	}
622 	if (parent->xp_netid) {
623 		xprt->xp_netid = (char *)strdup(parent->xp_netid);
624 		if (xprt->xp_netid == NULL) {
625 			syslog(LOG_ERR, "svc_fd_xprtcopy: strdup failed");
626 			if (xprt->xp_tp)
627 				free(xprt->xp_tp);
628 			svc_fd_xprtfree(xprt);
629 			return (NULL);
630 		}
631 	}
632 	/*
633 	 * share local and remote addresses with parent
634 	 */
635 	xprt->xp_ltaddr = parent->xp_ltaddr;
636 	xprt->xp_rtaddr = parent->xp_rtaddr;
637 	xprt->xp_type = parent->xp_type;
638 
639 	if ((cd = malloc(sizeof (struct cf_conn))) == NULL) {
640 		svc_fd_xprtfree(xprt);
641 		return (NULL);
642 	}
643 /* LINTED pointer alignment */
644 	pcd = (struct cf_conn *)parent->xp_p1;
645 	cd->sendsize = pcd->sendsize;
646 	cd->recvsize = pcd->recvsize;
647 	cd->strm_stat = pcd->strm_stat;
648 	cd->x_id = pcd->x_id;
649 	cd->cf_tsdu = pcd->cf_tsdu;
650 	cd->cf_cache = pcd->cf_cache;
651 	cd->cf_conn_nonblock = pcd->cf_conn_nonblock;
652 	cd->cf_conn_nonblock_timestamp = pcd->cf_conn_nonblock_timestamp;
653 	cd->xdrs.x_ops = NULL;
654 	xdrrec_create(&(cd->xdrs), cd->sendsize, 0, (caddr_t)xprt,
655 			(int(*)())NULL, (int(*)(void *, char *, int))write_vc);
656 	if (cd->xdrs.x_ops == NULL) {
657 		free(cd);
658 		svc_fd_xprtfree(xprt);
659 		return (NULL);
660 	}
661 	xprt->xp_verf.oa_base = cd->verf_body;
662 	xprt->xp_p1 = (char *)cd;
663 	xprt->xp_p2 = parent->xp_p2;	/* shared */
664 
665 	return (xprt);
666 }
667 
668 /*
669  * This routine is called by svc_getreqset(), when a packet is recd.
670  * The listener process creates another end point on which the actual
671  * connection is carried. It returns FALSE to indicate that it was
672  * not a rpc packet (falsely though), but as a side effect creates
673  * another endpoint which is also registered, which then always
674  * has a request ready to be served.
675  */
676 /* ARGSUSED1 */
677 static bool_t
678 rendezvous_request(SVCXPRT *xprt, struct rpc_msg *msg)
679 {
680 	struct cf_rendezvous *r;
681 	char *tpname = NULL;
682 	char devbuf[256];
683 	static void do_accept();
684 
685 /* LINTED pointer alignment */
686 	r = (struct cf_rendezvous *)xprt->xp_p1;
687 
688 again:
689 	switch (t_look(xprt->xp_fd)) {
690 	case T_DISCONNECT:
691 		(void) t_rcvdis(xprt->xp_fd, NULL);
692 		return (FALSE);
693 
694 	case T_LISTEN:
695 
696 		if (t_listen(xprt->xp_fd, r->t_call) == -1) {
697 			if ((t_errno == TSYSERR) && (errno == EINTR))
698 				goto again;
699 
700 			if (t_errno == TLOOK) {
701 				if (t_look(xprt->xp_fd) == T_DISCONNECT)
702 				    (void) t_rcvdis(xprt->xp_fd, NULL);
703 			}
704 			return (FALSE);
705 		}
706 		break;
707 	default:
708 		return (FALSE);
709 	}
710 	/*
711 	 * Now create another endpoint, and accept the connection
712 	 * on it.
713 	 */
714 
715 	if (xprt->xp_tp) {
716 		tpname = xprt->xp_tp;
717 	} else {
718 		/*
719 		 * If xprt->xp_tp is NULL, then try to extract the
720 		 * transport protocol information from the transport
721 		 * protcol corresponding to xprt->xp_fd
722 		 */
723 		struct netconfig *nconf;
724 		tpname = devbuf;
725 		if ((nconf = __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type))
726 				== NULL) {
727 			(void) syslog(LOG_ERR, errstring,
728 					rendezvous_request_str,
729 					"no suitable transport");
730 			goto err;
731 		}
732 		(void) strcpy(tpname, nconf->nc_device);
733 		freenetconfigent(nconf);
734 	}
735 
736 	do_accept(xprt->xp_fd, tpname, xprt->xp_netid, r->t_call, r);
737 
738 err:
739 	return (FALSE); /* there is never an rpc msg to be processed */
740 }
741 
742 static void
743 do_accept(int srcfd, char *tpname, char *netid, struct t_call *tcp,
744     struct cf_rendezvous *r)
745 {
746 	int	destfd;
747 	struct t_call	t_call;
748 	struct t_call	*tcp2 = NULL;
749 	struct t_info	tinfo;
750 	SVCXPRT	*xprt = NULL;
751 	SVCXPRT	*xprt_srcfd = NULL;
752 	char *option, *option_ret;
753 	struct opthdr *opt;
754 	struct t_optmgmt optreq, optret;
755 	int *p_optval;
756 
757 	destfd = t_open(tpname, O_RDWR, &tinfo);
758 	if (check_nonblock_timestamps) {
759 		if (destfd == -1 && t_errno == TSYSERR && errno == EMFILE) {
760 			/*
761 			 * Since there are nonblocking connection xprts and
762 			 * too many open files, the LRU connection xprt should
763 			 * get destroyed in case an attacker has been creating
764 			 * many connections.
765 			 */
766 			(void) mutex_lock(&svc_mutex);
767 			svc_timeout_nonblock_xprt_and_LRU(TRUE);
768 			(void) mutex_unlock(&svc_mutex);
769 			destfd = t_open(tpname, O_RDWR, &tinfo);
770 		} else {
771 			/*
772 			 * Destroy/timeout all nonblock connection xprts
773 			 * that have not had recent activity.
774 			 * Do not destroy LRU xprt unless there are
775 			 * too many open files.
776 			 */
777 			(void) mutex_lock(&svc_mutex);
778 			svc_timeout_nonblock_xprt_and_LRU(FALSE);
779 			(void) mutex_unlock(&svc_mutex);
780 		}
781 	}
782 	if (destfd == -1) {
783 		char errorstr[100];
784 
785 		__tli_sys_strerror(errorstr, sizeof (errorstr), t_errno,
786 			errno);
787 		(void) syslog(LOG_ERR, "%s : %s : %s", do_accept_str,
788 				"can't open connection", errorstr);
789 		(void) t_snddis(srcfd, tcp);
790 		return;
791 	}
792 	if (destfd < 256) {
793 		int nfd;
794 
795 		nfd = fcntl(destfd, F_DUPFD, 256);
796 		if (nfd != -1) {
797 			if (t_close(destfd) == -1) {
798 				char errorstr[100];
799 
800 				__tli_sys_strerror(errorstr, sizeof (errorstr),
801 						t_errno, errno);
802 				(void) syslog(LOG_ERR,
803 		"could not t_close() old fd %d; mem & fd leak error: %s",
804 						destfd, errorstr);
805 			}
806 			destfd = nfd;
807 			if (t_sync(destfd) == -1) {
808 				char errorstr[100];
809 
810 				__tli_sys_strerror(errorstr, sizeof (errorstr),
811 						t_errno, errno);
812 				(void) syslog(LOG_ERR,
813 				    "could not t_sync() duped fd %d: %s",
814 						destfd, errorstr);
815 				(void) t_snddis(srcfd, tcp);
816 				return;
817 			}
818 		}
819 	}
820 	if (RPC_FD_NOTIN_FDSET(destfd)) {
821 		(void) syslog(LOG_ERR, errstring, do_accept_str,
822 						svc_vc_fderr);
823 		(void) t_close(destfd);
824 		(void) t_snddis(srcfd, tcp);
825 		errno = EBADF;
826 		t_errno = TBADF;
827 		return;
828 	}
829 	(void) fcntl(destfd, F_SETFD, 1); /* make it "close on exec" */
830 	if ((tinfo.servtype != T_COTS) && (tinfo.servtype != T_COTS_ORD)) {
831 		/* Not a connection oriented mode */
832 		(void) syslog(LOG_ERR, errstring, do_accept_str,
833 				"do_accept:  illegal transport");
834 		(void) t_close(destfd);
835 		(void) t_snddis(srcfd, tcp);
836 		return;
837 	}
838 
839 
840 	if (t_bind(destfd, NULL, r->t_bind) == -1) {
841 		char errorstr[100];
842 
843 		__tli_sys_strerror(errorstr, sizeof (errorstr), t_errno,
844 				errno);
845 		(void) syslog(LOG_ERR, " %s : %s : %s", do_accept_str,
846 			"t_bind failed", errorstr);
847 		(void) t_close(destfd);
848 		(void) t_snddis(srcfd, tcp);
849 		return;
850 	}
851 
852 	if (r->tcp_flag)	/* if TCP, set NODELAY flag */
853 		(void) __td_setnodelay(destfd);
854 
855 	/*
856 	 * This connection is not listening, hence no need to set
857 	 * the qlen.
858 	 */
859 
860 	/*
861 	 * XXX: The local transport chokes on its own listen
862 	 * options so we zero them for now
863 	 */
864 	t_call = *tcp;
865 	t_call.opt.len = 0;
866 	t_call.opt.maxlen = 0;
867 	t_call.opt.buf = NULL;
868 
869 	while (t_accept(srcfd, destfd, &t_call) == -1) {
870 		char errorstr[100];
871 
872 		switch (t_errno) {
873 		case TLOOK:
874 again:
875 			switch (t_look(srcfd)) {
876 			case T_CONNECT:
877 			case T_DATA:
878 			case T_EXDATA:
879 				/* this should not happen */
880 				break;
881 
882 			case T_DISCONNECT:
883 				(void) t_rcvdis(srcfd, NULL);
884 				break;
885 
886 			case T_LISTEN:
887 				if (tcp2 == NULL)
888 /* LINTED pointer alignment */
889 					tcp2 = (struct t_call *)t_alloc(srcfd,
890 					    T_CALL, T_ADDR | T_OPT);
891 				if (tcp2 == NULL) {
892 
893 					(void) t_close(destfd);
894 					(void) t_snddis(srcfd, tcp);
895 					syslog(LOG_ERR, errstring,
896 						do_accept_str, no_mem_str);
897 					return;
898 					/* NOTREACHED */
899 				}
900 				if (t_listen(srcfd, tcp2) == -1) {
901 					switch (t_errno) {
902 					case TSYSERR:
903 						if (errno == EINTR)
904 							goto again;
905 						break;
906 
907 					case TLOOK:
908 						goto again;
909 					}
910 					(void) t_free((char *)tcp2, T_CALL);
911 					(void) t_close(destfd);
912 					(void) t_snddis(srcfd, tcp);
913 					return;
914 					/* NOTREACHED */
915 				}
916 				do_accept(srcfd, tpname, netid, tcp2, r);
917 				break;
918 
919 			case T_ORDREL:
920 				(void) t_rcvrel(srcfd);
921 				(void) t_sndrel(srcfd);
922 				break;
923 			}
924 			if (tcp2) {
925 				(void) t_free((char *)tcp2, T_CALL);
926 				tcp2 = NULL;
927 			}
928 			break;
929 
930 		case TBADSEQ:
931 			/*
932 			 * This can happen if the remote side has
933 			 * disconnected before the connection is
934 			 * accepted.  In this case, a disconnect
935 			 * should not be sent on srcfd (important!
936 			 * the listening fd will be hosed otherwise!).
937 			 * This error is not logged since this is an
938 			 * operational situation that is recoverable.
939 			 */
940 			(void) t_close(destfd);
941 			return;
942 			/* NOTREACHED */
943 
944 		case TOUTSTATE:
945 			/*
946 			 * This can happen if the t_rcvdis() or t_rcvrel()/
947 			 * t_sndrel() put srcfd into the T_IDLE state.
948 			 */
949 			if (t_getstate(srcfd) == T_IDLE) {
950 				(void) t_close(destfd);
951 				(void) t_snddis(srcfd, tcp);
952 				return;
953 			}
954 			/* else FALL THROUGH TO */
955 
956 		default:
957 			__tli_sys_strerror(errorstr, sizeof (errorstr),
958 					t_errno, errno);
959 			(void) syslog(LOG_ERR,
960 			    "cannot accept connection:  %s (current state %d)",
961 			    errorstr, t_getstate(srcfd));
962 			(void) t_close(destfd);
963 			(void) t_snddis(srcfd, tcp);
964 			return;
965 			/* NOTREACHED */
966 		}
967 	}
968 
969 	if (r->tcp_flag && r->tcp_keepalive) {
970 		option = malloc(sizeof (struct opthdr) + sizeof (int));
971 		option_ret = malloc(sizeof (struct opthdr) + sizeof (int));
972 		if (option && option_ret) {
973 			/* LINTED pointer cast */
974 			opt = (struct opthdr *)option;
975 			opt->level = SOL_SOCKET;
976 			opt->name  = SO_KEEPALIVE;
977 			opt->len  = sizeof (int);
978 			p_optval = (int *)(opt + 1);
979 			*p_optval = SO_KEEPALIVE;
980 			optreq.opt.maxlen = optreq.opt.len =
981 				sizeof (struct opthdr) + sizeof (int);
982 			optreq.opt.buf = (char *)option;
983 			optreq.flags = T_NEGOTIATE;
984 			optret.opt.maxlen = sizeof (struct opthdr)
985 					+ sizeof (int);
986 			optret.opt.buf = (char *)option_ret;
987 			(void) t_optmgmt(destfd, &optreq, &optret);
988 			free(option);
989 			free(option_ret);
990 		} else {
991 			if (option)
992 				free(option);
993 			if (option_ret)
994 				free(option_ret);
995 		}
996 	}
997 
998 
999 	/*
1000 	 * make a new transporter
1001 	 */
1002 	xprt = makefd_xprt(destfd, r->sendsize, r->recvsize, r->cf_tsdu,
1003 				r->cf_cache);
1004 	if (xprt == NULL) {
1005 		/*
1006 		 * makefd_xprt() returns a NULL xprt only when
1007 		 * it's out of memory.
1008 		 */
1009 		goto memerr;
1010 	}
1011 
1012 	/*
1013 	 * Copy the new local and remote bind information
1014 	 */
1015 
1016 	xprt->xp_rtaddr.len = tcp->addr.len;
1017 	xprt->xp_rtaddr.maxlen = tcp->addr.len;
1018 	if ((xprt->xp_rtaddr.buf = malloc(tcp->addr.len)) == NULL)
1019 		goto memerr;
1020 	(void) memcpy(xprt->xp_rtaddr.buf, tcp->addr.buf, tcp->addr.len);
1021 
1022 	if (strcmp(netid, "tcp") == 0) {
1023 		xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_in);
1024 		if ((xprt->xp_ltaddr.buf =
1025 			malloc(xprt->xp_ltaddr.maxlen)) == NULL)
1026 			goto memerr;
1027 		if (t_getname(destfd, &xprt->xp_ltaddr, LOCALNAME) < 0) {
1028 		    (void) syslog(LOG_ERR,
1029 				"do_accept: t_getname for tcp failed!");
1030 			goto xprt_err;
1031 		}
1032 	} else if (strcmp(netid, "tcp6") == 0) {
1033 		xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_in6);
1034 		if ((xprt->xp_ltaddr.buf =
1035 			malloc(xprt->xp_ltaddr.maxlen)) == NULL)
1036 			goto memerr;
1037 		if (t_getname(destfd, &xprt->xp_ltaddr, LOCALNAME) < 0) {
1038 			(void) syslog(LOG_ERR,
1039 				"do_accept: t_getname for tcp6 failed!");
1040 			goto xprt_err;
1041 		}
1042 	}
1043 
1044 	xprt->xp_tp = strdup(tpname);
1045 	xprt->xp_netid = strdup(netid);
1046 	if ((xprt->xp_tp == NULL) ||
1047 	    (xprt->xp_netid == NULL)) {
1048 		goto memerr;
1049 	}
1050 	if (tcp->opt.len > 0) {
1051 		struct netbuf *netptr;
1052 
1053 		xprt->xp_p2 = malloc(sizeof (struct netbuf));
1054 
1055 		if (xprt->xp_p2 != NULL) {
1056 /* LINTED pointer alignment */
1057 			netptr = (struct netbuf *)xprt->xp_p2;
1058 
1059 			netptr->len = tcp->opt.len;
1060 			netptr->maxlen = tcp->opt.len;
1061 			if ((netptr->buf = malloc(tcp->opt.len)) == NULL)
1062 				goto memerr;
1063 			(void) memcpy(netptr->buf, tcp->opt.buf, tcp->opt.len);
1064 		} else
1065 			goto memerr;
1066 	}
1067 /*	(void) ioctl(destfd, I_POP, NULL);    */
1068 
1069 	/*
1070 	 * If a nonblocked connection fd has been requested,
1071 	 * perform the necessary operations.
1072 	 */
1073 	xprt_srcfd = svc_xports[srcfd];
1074 	/* LINTED pointer cast */
1075 	if (((struct cf_rendezvous *)(xprt_srcfd->xp_p1))->cf_connmaxrec) {
1076 		if (!svc_vc_nonblock(xprt_srcfd, xprt))
1077 			goto xprt_err;
1078 	}
1079 
1080 	/*
1081 	 * Copy the call back declared for the service to the current
1082 	 * connection
1083 	 */
1084 	xprt->xp_closeclnt = xprt_srcfd->xp_closeclnt;
1085 	xprt_register(xprt);
1086 
1087 	return;
1088 
1089 memerr:
1090 	(void) syslog(LOG_ERR, errstring, do_accept_str, no_mem_str);
1091 xprt_err:
1092 	if (xprt)
1093 		svc_vc_destroy(xprt);
1094 	(void) t_close(destfd);
1095 }
1096 
1097 /*
1098  * This routine performs the necessary fcntl() operations to create
1099  * a nonblocked connection fd.
1100  * It also adjusts the sizes and allocates the buffer
1101  * for the nonblocked operations, and updates the associated
1102  * timestamp field in struct cf_conn for timeout bookkeeping.
1103  */
1104 static bool_t
1105 svc_vc_nonblock(SVCXPRT *xprt_rendezvous, SVCXPRT *xprt_conn)
1106 {
1107 	int nn;
1108 	int fdconn = xprt_conn->xp_fd;
1109 	struct cf_rendezvous *r =
1110 		/* LINTED pointer cast */
1111 		(struct cf_rendezvous *)xprt_rendezvous->xp_p1;
1112 	/* LINTED pointer cast */
1113 	struct cf_conn *cd = (struct cf_conn *)xprt_conn->xp_p1;
1114 	uint32_t maxrecsz;
1115 
1116 	if ((nn = fcntl(fdconn, F_GETFL, 0)) < 0) {
1117 		(void) syslog(LOG_ERR, "%s : %s : %m", do_accept_str,
1118 			    no_fcntl_getfl_str);
1119 		return (FALSE);
1120 	}
1121 
1122 	if (fcntl(fdconn, F_SETFL, nn|O_NONBLOCK) != 0) {
1123 		(void) syslog(LOG_ERR, "%s : %s : %m", do_accept_str,
1124 				no_nonblock_str);
1125 		return (FALSE);
1126 	}
1127 
1128 	cd->cf_conn_nonblock = TRUE;
1129 	/*
1130 	 * If the max fragment size has not been set via
1131 	 * rpc_control(), use the default.
1132 	 */
1133 	if ((maxrecsz = r->cf_connmaxrec) == 0)
1134 		maxrecsz = r->recvsize;
1135 	/* Set XDR stream to use non-blocking semantics. */
1136 	if (__xdrrec_set_conn_nonblock(svc_xdrs[fdconn], maxrecsz)) {
1137 		check_nonblock_timestamps = TRUE;
1138 		update_nonblock_timestamps(xprt_conn);
1139 		return (TRUE);
1140 	}
1141 	return (FALSE);
1142 }
1143 
1144 /* ARGSUSED */
1145 static enum xprt_stat
1146 rendezvous_stat(SVCXPRT *xprt)
1147 {
1148 	return (XPRT_IDLE);
1149 }
1150 
1151 static void
1152 svc_vc_destroy(SVCXPRT *xprt)
1153 {
1154 	(void) mutex_lock(&svc_mutex);
1155 	_svc_vc_destroy_private(xprt, TRUE);
1156 	(void) svc_timeout_nonblock_xprt_and_LRU(FALSE);
1157 	(void) mutex_unlock(&svc_mutex);
1158 }
1159 
1160 void
1161 _svc_vc_destroy_private(SVCXPRT *xprt, bool_t lock_not_held)
1162 {
1163 	if (svc_mt_mode != RPC_SVC_MT_NONE) {
1164 /* LINTED pointer alignment */
1165 		if (SVCEXT(xprt)->parent)
1166 /* LINTED pointer alignment */
1167 			xprt = SVCEXT(xprt)->parent;
1168 /* LINTED pointer alignment */
1169 		svc_flags(xprt) |= SVC_DEFUNCT;
1170 /* LINTED pointer alignment */
1171 		if (SVCEXT(xprt)->refcnt > 0)
1172 			return;
1173 	}
1174 
1175 	if (xprt->xp_closeclnt != NULL) {
1176 		svc_errorhandler_t cb = xprt->xp_closeclnt;
1177 
1178 		/*
1179 		 * Reset the pointer here to avoid reentrance on the same
1180 		 * SVCXPRT handle.
1181 		 */
1182 		xprt->xp_closeclnt = NULL;
1183 		cb(xprt, (xprt->xp_rtaddr.len != 0));
1184 	}
1185 
1186 	__xprt_unregister_private(xprt, lock_not_held);
1187 	(void) t_close(xprt->xp_fd);
1188 
1189 	(void) mutex_lock(&timestamp_lock);
1190 	if (timestamps && xprt->xp_fd < ntimestamps) {
1191 		timestamps[xprt->xp_fd] = 0;
1192 	}
1193 	(void) mutex_unlock(&timestamp_lock);
1194 
1195 	if (svc_mt_mode != RPC_SVC_MT_NONE) {
1196 		svc_xprt_destroy(xprt);
1197 	} else {
1198 /* LINTED pointer alignment */
1199 		if (svc_type(xprt) == SVC_RENDEZVOUS)
1200 			svc_vc_xprtfree(xprt);
1201 		else
1202 			svc_fd_xprtfree(xprt);
1203 	}
1204 }
1205 
1206 /*ARGSUSED*/
1207 static bool_t
1208 svc_vc_control(SVCXPRT *xprt, const uint_t rq, void *in)
1209 {
1210 	switch (rq) {
1211 	case SVCSET_RECVERRHANDLER:
1212 		xprt->xp_closeclnt = (svc_errorhandler_t)in;
1213 		return (TRUE);
1214 	case SVCGET_RECVERRHANDLER:
1215 		*(svc_errorhandler_t *)in = xprt->xp_closeclnt;
1216 		return (TRUE);
1217 	case SVCGET_XID:
1218 		if (xprt->xp_p1 == NULL)
1219 			return (FALSE);
1220 		/* LINTED pointer alignment */
1221 		*(uint32_t *)in = ((struct cf_conn *)(xprt->xp_p1))->x_id;
1222 		return (TRUE);
1223 	default:
1224 		return (FALSE);
1225 	}
1226 }
1227 
1228 static bool_t
1229 rendezvous_control(SVCXPRT *xprt, const uint_t rq, void *in)
1230 {
1231 	struct cf_rendezvous *r;
1232 	int tmp;
1233 
1234 	switch (rq) {
1235 	case SVCSET_RECVERRHANDLER:
1236 		xprt->xp_closeclnt = (svc_errorhandler_t)in;
1237 		return (TRUE);
1238 	case SVCGET_RECVERRHANDLER:
1239 		*(svc_errorhandler_t *)in = xprt->xp_closeclnt;
1240 		return (TRUE);
1241 	case SVCSET_KEEPALIVE:
1242 		/* LINTED pointer cast */
1243 		r = (struct cf_rendezvous *)xprt->xp_p1;
1244 		if (r->tcp_flag) {
1245 			r->tcp_keepalive = (int)(intptr_t)in;
1246 			return (TRUE);
1247 		}
1248 		return (FALSE);
1249 	case SVCSET_CONNMAXREC:
1250 		/*
1251 		 * Override the default maximum record size, set via
1252 		 * rpc_control(), for this connection. Only appropriate
1253 		 * for connection oriented transports, but is ignored for
1254 		 * the connectionless case, so no need to check the
1255 		 * connection type here.
1256 		 */
1257 		/* LINTED pointer cast */
1258 		r = (struct cf_rendezvous *)xprt->xp_p1;
1259 		tmp = __rpc_legal_connmaxrec(*(int *)in);
1260 		if (r != 0 && tmp >= 0) {
1261 			r->cf_connmaxrec = tmp;
1262 			return (TRUE);
1263 		}
1264 		return (FALSE);
1265 	case SVCGET_CONNMAXREC:
1266 		/* LINTED pointer cast */
1267 		r = (struct cf_rendezvous *)xprt->xp_p1;
1268 		if (r != 0) {
1269 			*(int *)in = r->cf_connmaxrec;
1270 			return (TRUE);
1271 		}
1272 		return (FALSE);
1273 	case SVCGET_XID:	/* fall through for now */
1274 	default:
1275 		return (FALSE);
1276 	}
1277 }
1278 
1279 /*
1280  * All read operations timeout after 35 seconds.
1281  * A timeout is fatal for the connection.
1282  * update_timestamps() is used by nisplus operations,
1283  * update_nonblock_timestamps() is used for nonblocked
1284  * connection fds.
1285  */
1286 #define	WAIT_PER_TRY	35000	/* milliseconds */
1287 
1288 static void
1289 update_timestamps(int fd)
1290 {
1291 	(void) mutex_lock(&timestamp_lock);
1292 	if (timestamps) {
1293 		struct timeval tv;
1294 
1295 		(void) gettimeofday(&tv, NULL);
1296 		while (fd >= ntimestamps) {
1297 			long *tmp_timestamps = timestamps;
1298 
1299 			/* allocate more timestamps */
1300 			tmp_timestamps = realloc(timestamps,
1301 				sizeof (long) *
1302 				(ntimestamps + FD_INCREMENT));
1303 			if (tmp_timestamps == NULL) {
1304 				(void) mutex_unlock(&timestamp_lock);
1305 				syslog(LOG_ERR,
1306 					"update_timestamps: out of memory");
1307 				return;
1308 			}
1309 
1310 			timestamps = tmp_timestamps;
1311 			(void) memset(&timestamps[ntimestamps], 0,
1312 				sizeof (long) * FD_INCREMENT);
1313 			ntimestamps += FD_INCREMENT;
1314 		}
1315 		timestamps[fd] = tv.tv_sec;
1316 	}
1317 	(void) mutex_unlock(&timestamp_lock);
1318 }
1319 
1320 static  void
1321 update_nonblock_timestamps(SVCXPRT *xprt_conn)
1322 {
1323 	struct timeval tv;
1324 	/* LINTED pointer cast */
1325 	struct cf_conn *cd = (struct cf_conn *)xprt_conn->xp_p1;
1326 
1327 	(void) gettimeofday(&tv, NULL);
1328 	cd->cf_conn_nonblock_timestamp = tv.tv_sec;
1329 }
1330 
1331 /*
1332  * reads data from the vc conection.
1333  * any error is fatal and the connection is closed.
1334  * (And a read of zero bytes is a half closed stream => error.)
1335  */
1336 static int
1337 read_vc(SVCXPRT *xprt, caddr_t buf, int len)
1338 {
1339 	int fd = xprt->xp_fd;
1340 	XDR *xdrs = svc_xdrs[fd];
1341 	struct pollfd pfd;
1342 	int ret;
1343 
1344 	/*
1345 	 * Make sure the connection is not already dead.
1346 	 */
1347 /* LINTED pointer alignment */
1348 	if (svc_failed(xprt))
1349 		return (-1);
1350 
1351 	/* LINTED pointer cast */
1352 	if (((struct cf_conn *)(xprt->xp_p1))->cf_conn_nonblock) {
1353 		/*
1354 		 * For nonblocked reads, only update the
1355 		 * timestamps to record the activity so the
1356 		 * connection will not be timedout.
1357 		 * Up to "len" bytes are requested.
1358 		 * If fewer than "len" bytes are received, the
1359 		 * connection is poll()ed again.
1360 		 * The poll() for the connection fd is performed
1361 		 * in the main poll() so that all outstanding fds
1362 		 * are polled rather than just the vc connection.
1363 		 * Polling on only the vc connection until the entire
1364 		 * fragment has been read can be exploited in
1365 		 * a Denial of Service Attack such as telnet <host> 111.
1366 		 */
1367 		if ((len = t_rcvnonblock(xprt, buf, len)) >= 0) {
1368 			if (len > 0) {
1369 				update_timestamps(fd);
1370 				update_nonblock_timestamps(xprt);
1371 			}
1372 			return (len);
1373 		}
1374 		goto fatal_err;
1375 	}
1376 
1377 	if (!__is_xdrrec_first(xdrs)) {
1378 
1379 		pfd.fd = fd;
1380 		pfd.events = MASKVAL;
1381 
1382 		do {
1383 			if ((ret = poll(&pfd, 1, WAIT_PER_TRY)) <= 0) {
1384 				/*
1385 				 * If errno is EINTR, ERESTART, or EAGAIN
1386 				 * ignore error and repeat poll
1387 				 */
1388 				if (ret < 0 && (errno == EINTR ||
1389 				    errno == ERESTART || errno == EAGAIN))
1390 					continue;
1391 				goto fatal_err;
1392 			}
1393 		} while (pfd.revents == 0);
1394 		if (pfd.revents & POLLNVAL)
1395 			goto fatal_err;
1396 	}
1397 	(void) __xdrrec_resetfirst(xdrs);
1398 	if ((len = t_rcvall(fd, buf, len)) > 0) {
1399 		update_timestamps(fd);
1400 		return (len);
1401 	}
1402 
1403 fatal_err:
1404 /* LINTED pointer alignment */
1405 	((struct cf_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
1406 /* LINTED pointer alignment */
1407 	svc_flags(xprt) |= SVC_FAILED;
1408 	return (-1);
1409 }
1410 
1411 /*
1412  * Requests up to "len" bytes of data.
1413  * Returns number of bytes actually received, or error indication.
1414  */
1415 static int
1416 t_rcvnonblock(SVCXPRT *xprt, caddr_t buf, int len)
1417 {
1418 	int fd = xprt->xp_fd;
1419 	int flag;
1420 	int res;
1421 
1422 	res = t_rcv(fd, buf, (unsigned)len, &flag);
1423 	if (res == -1) {
1424 		switch (t_errno) {
1425 		case TLOOK:
1426 			switch (t_look(fd)) {
1427 			case T_DISCONNECT:
1428 				(void) t_rcvdis(fd, NULL);
1429 				break;
1430 			case T_ORDREL:
1431 				(void) t_rcvrel(fd);
1432 				(void) t_sndrel(fd);
1433 				break;
1434 			default:
1435 				break;
1436 			}
1437 			break;
1438 		case TNODATA:
1439 			/*
1440 			 * Either poll() lied, or the xprt/fd was closed and
1441 			 * re-opened under our feet. Return 0, so that we go
1442 			 * back to waiting for data.
1443 			 */
1444 			res = 0;
1445 			break;
1446 		/* Should handle TBUFOVFLW TSYSERR ? */
1447 		default:
1448 			break;
1449 		}
1450 	}
1451 	return (res);
1452 }
1453 
1454 /*
1455  * Timeout out nonblocked connection fds
1456  * If there has been no activity on the fd for __rpc_irtimeout
1457  * seconds, timeout the fd  by destroying its xprt.
1458  * If the caller gets an EMFILE error, the caller may also request
1459  * that the least busy xprt gets destroyed as well.
1460  * svc_thr_mutex is held when this is called.
1461  * svc_mutex is held when this is called.
1462  */
1463 static void
1464 svc_timeout_nonblock_xprt_and_LRU(bool_t destroy_lru)
1465 {
1466 	SVCXPRT *xprt;
1467 	SVCXPRT *dead_xprt[CLEANUP_SIZE];
1468 	SVCXPRT *candidate_xprt = NULL;
1469 	struct cf_conn *cd;
1470 	int i, fd_idx = 0, dead_idx = 0;
1471 	struct timeval now;
1472 	time_t lasttime, maxctime = 0;
1473 	extern rwlock_t svc_fd_lock;
1474 
1475 	if (!check_nonblock_timestamps)
1476 		return;
1477 
1478 	(void) gettimeofday(&now, NULL);
1479 	if (svc_xports == NULL)
1480 		return;
1481 	/*
1482 	 * Hold svc_fd_lock to protect
1483 	 * svc_xports, svc_maxpollfd, svc_max_pollfd
1484 	 */
1485 	(void) rw_wrlock(&svc_fd_lock);
1486 	for (;;) {
1487 		/*
1488 		 * Timeout upto CLEANUP_SIZE connection fds per
1489 		 * iteration for the while(1) loop
1490 		 */
1491 		for (dead_idx = 0; fd_idx < svc_max_pollfd; fd_idx++) {
1492 			if ((xprt = svc_xports[fd_idx]) == NULL) {
1493 				continue;
1494 			}
1495 			/* Only look at connection fds */
1496 			/* LINTED pointer cast */
1497 			if (svc_type(xprt) != SVC_CONNECTION) {
1498 				continue;
1499 			}
1500 			/* LINTED pointer cast */
1501 			cd = (struct cf_conn *)xprt->xp_p1;
1502 			if (!cd->cf_conn_nonblock)
1503 				continue;
1504 			lasttime = now.tv_sec - cd->cf_conn_nonblock_timestamp;
1505 			if (lasttime >= __rpc_irtimeout &&
1506 			    __rpc_irtimeout != 0) {
1507 				/* Enter in timedout/dead array */
1508 				dead_xprt[dead_idx++] = xprt;
1509 				if (dead_idx >= CLEANUP_SIZE)
1510 					break;
1511 			} else
1512 			if (lasttime > maxctime) {
1513 				/* Possible LRU xprt */
1514 				candidate_xprt = xprt;
1515 				maxctime = lasttime;
1516 			}
1517 		}
1518 
1519 		for (i = 0; i < dead_idx; i++) {
1520 			/* Still holding svc_fd_lock */
1521 			_svc_vc_destroy_private(dead_xprt[i], FALSE);
1522 		}
1523 
1524 		/*
1525 		 * If all the nonblocked fds have been checked, we're done.
1526 		 */
1527 		if (fd_idx++ >= svc_max_pollfd)
1528 			break;
1529 	}
1530 	if ((destroy_lru) && (candidate_xprt != NULL)) {
1531 		_svc_vc_destroy_private(candidate_xprt, FALSE);
1532 	}
1533 	(void) rw_unlock(&svc_fd_lock);
1534 }
1535 /*
1536  * Receive the required bytes of data, even if it is fragmented.
1537  */
1538 static int
1539 t_rcvall(int fd, char *buf, int len)
1540 {
1541 	int flag;
1542 	int final = 0;
1543 	int res;
1544 
1545 	do {
1546 		res = t_rcv(fd, buf, (unsigned)len, &flag);
1547 		if (res == -1) {
1548 			if (t_errno == TLOOK) {
1549 				switch (t_look(fd)) {
1550 				case T_DISCONNECT:
1551 					(void) t_rcvdis(fd, NULL);
1552 					break;
1553 				case T_ORDREL:
1554 					(void) t_rcvrel(fd);
1555 					(void) t_sndrel(fd);
1556 					break;
1557 				default:
1558 					break;
1559 				}
1560 			}
1561 			break;
1562 		}
1563 		final += res;
1564 		buf += res;
1565 		len -= res;
1566 	} while (len && (flag & T_MORE));
1567 	return (res == -1 ? -1 : final);
1568 }
1569 
1570 /*
1571  * writes data to the vc connection.
1572  * Any error is fatal and the connection is closed.
1573  */
1574 static int
1575 write_vc(SVCXPRT *xprt, caddr_t buf, int len)
1576 {
1577 	int i, cnt;
1578 	int flag;
1579 	int maxsz;
1580 	int nonblock;
1581 	struct pollfd pfd;
1582 
1583 /* LINTED pointer alignment */
1584 	maxsz = ((struct cf_conn *)(xprt->xp_p1))->cf_tsdu;
1585 	/* LINTED pointer cast */
1586 	nonblock = ((struct cf_conn *)(xprt->xp_p1))->cf_conn_nonblock;
1587 	if (nonblock && maxsz <= 0)
1588 		maxsz = len;
1589 	if ((maxsz == 0) || (maxsz == -1)) {
1590 		if ((len = t_snd(xprt->xp_fd, buf, (unsigned)len,
1591 				(int)0)) == -1) {
1592 			if (t_errno == TLOOK) {
1593 				switch (t_look(xprt->xp_fd)) {
1594 				case T_DISCONNECT:
1595 					(void) t_rcvdis(xprt->xp_fd, NULL);
1596 					break;
1597 				case T_ORDREL:
1598 					(void) t_rcvrel(xprt->xp_fd);
1599 					(void) t_sndrel(xprt->xp_fd);
1600 					break;
1601 				default:
1602 					break;
1603 				}
1604 			}
1605 /* LINTED pointer alignment */
1606 			((struct cf_conn *)(xprt->xp_p1))->strm_stat
1607 					= XPRT_DIED;
1608 /* LINTED pointer alignment */
1609 			svc_flags(xprt) |= SVC_FAILED;
1610 		}
1611 		return (len);
1612 	}
1613 
1614 	/*
1615 	 * Setup for polling. We want to be able to write normal
1616 	 * data to the transport
1617 	 */
1618 	pfd.fd = xprt->xp_fd;
1619 	pfd.events = POLLWRNORM;
1620 
1621 	/*
1622 	 * This for those transports which have a max size for data,
1623 	 * and for the non-blocking case, where t_snd() may send less
1624 	 * than requested.
1625 	 */
1626 	for (cnt = len, i = 0; cnt > 0; cnt -= i, buf += i) {
1627 		flag = cnt > maxsz ? T_MORE : 0;
1628 		if ((i = t_snd(xprt->xp_fd, buf,
1629 			(unsigned)MIN(cnt, maxsz), flag)) == -1) {
1630 			if (t_errno == TLOOK) {
1631 				switch (t_look(xprt->xp_fd)) {
1632 				case T_DISCONNECT:
1633 					(void) t_rcvdis(xprt->xp_fd, NULL);
1634 					break;
1635 				case T_ORDREL:
1636 					(void) t_rcvrel(xprt->xp_fd);
1637 					break;
1638 				default:
1639 					break;
1640 				}
1641 			} else if (t_errno == TFLOW) {
1642 				/* Try again */
1643 				i = 0;
1644 				/* Wait till we can write to the transport */
1645 				do {
1646 				    if (poll(&pfd, 1, WAIT_PER_TRY) < 0) {
1647 					/*
1648 					 * If errno is ERESTART, or
1649 					 * EAGAIN ignore error and repeat poll
1650 					 */
1651 					if (errno == ERESTART ||
1652 					    errno == EAGAIN)
1653 						continue;
1654 					else
1655 						goto fatal_err;
1656 				    }
1657 				} while (pfd.revents == 0);
1658 				if (pfd.revents & (POLLNVAL | POLLERR |
1659 						    POLLHUP))
1660 					goto fatal_err;
1661 				continue;
1662 			}
1663 fatal_err:
1664 /* LINTED pointer alignment */
1665 			((struct cf_conn *)(xprt->xp_p1))->strm_stat
1666 					= XPRT_DIED;
1667 /* LINTED pointer alignment */
1668 			svc_flags(xprt) |= SVC_FAILED;
1669 			return (-1);
1670 		}
1671 	}
1672 	return (len);
1673 }
1674 
1675 static enum xprt_stat
1676 svc_vc_stat(SVCXPRT *xprt)
1677 {
1678 /* LINTED pointer alignment */
1679 	SVCXPRT *parent = SVCEXT(xprt)->parent ? SVCEXT(xprt)->parent : xprt;
1680 
1681 /* LINTED pointer alignment */
1682 	if (svc_failed(parent) || svc_failed(xprt))
1683 		return (XPRT_DIED);
1684 	if (!xdrrec_eof(svc_xdrs[xprt->xp_fd]))
1685 		return (XPRT_MOREREQS);
1686 	/*
1687 	 * xdrrec_eof could have noticed that the connection is dead, so
1688 	 * check status again.
1689 	 */
1690 /* LINTED pointer alignment */
1691 	if (svc_failed(parent) || svc_failed(xprt))
1692 		return (XPRT_DIED);
1693 	return (XPRT_IDLE);
1694 }
1695 
1696 
1697 
1698 static bool_t
1699 svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
1700 {
1701 /* LINTED pointer alignment */
1702 	struct cf_conn *cd = (struct cf_conn *)(xprt->xp_p1);
1703 	XDR *xdrs = svc_xdrs[xprt->xp_fd];
1704 
1705 	xdrs->x_op = XDR_DECODE;
1706 
1707 	if (cd->cf_conn_nonblock) {
1708 		/* Get the next input */
1709 		if (!__xdrrec_getbytes_nonblock(xdrs, &cd->strm_stat)) {
1710 			/*
1711 			 * The entire record has not been received.
1712 			 * If the xprt has died, pass it along in svc_flags.
1713 			 * Return FALSE; For nonblocked vc connection,
1714 			 * xdr_callmsg() is called only after the entire
1715 			 * record has been received.  For blocked vc
1716 			 * connection, the data is received on the fly as it
1717 			 * is being processed through the xdr routines.
1718 			 */
1719 			if (cd->strm_stat == XPRT_DIED)
1720 				/* LINTED pointer cast */
1721 				svc_flags(xprt) |= SVC_FAILED;
1722 			return (FALSE);
1723 		}
1724 	} else {
1725 		if (!xdrrec_skiprecord(xdrs))
1726 			return (FALSE);
1727 		(void) __xdrrec_setfirst(xdrs);
1728 	}
1729 
1730 	if (xdr_callmsg(xdrs, msg)) {
1731 		cd->x_id = msg->rm_xid;
1732 		return (TRUE);
1733 	}
1734 
1735 	/*
1736 	 * If a non-blocking connection, drop it when message decode fails.
1737 	 * We are either under attack, or we're talking to a broken client.
1738 	 */
1739 	if (cd->cf_conn_nonblock) {
1740 		/* LINTED pointer cast */
1741 		svc_flags(xprt) |= SVC_FAILED;
1742 	}
1743 
1744 	return (FALSE);
1745 }
1746 
1747 static bool_t
1748 svc_vc_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
1749 {
1750 	bool_t dummy;
1751 
1752 /* LINTED pointer alignment */
1753 	dummy = SVCAUTH_UNWRAP(&SVC_XP_AUTH(xprt), svc_xdrs[xprt->xp_fd],
1754 							xdr_args, args_ptr);
1755 	if (svc_mt_mode != RPC_SVC_MT_NONE)
1756 		svc_args_done(xprt);
1757 	return (dummy);
1758 }
1759 
1760 static bool_t
1761 svc_vc_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
1762 {
1763 /* LINTED pointer alignment */
1764 	XDR *xdrs = &(((struct cf_conn *)(xprt->xp_p1))->xdrs);
1765 
1766 	xdrs->x_op = XDR_FREE;
1767 	return ((*xdr_args)(xdrs, args_ptr));
1768 }
1769 
1770 static bool_t
1771 svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg)
1772 {
1773 /* LINTED pointer alignment */
1774 	struct cf_conn *cd = (struct cf_conn *)(xprt->xp_p1);
1775 	XDR *xdrs = &(cd->xdrs);
1776 	bool_t stat = FALSE;
1777 	xdrproc_t xdr_results;
1778 	caddr_t xdr_location;
1779 	bool_t has_args;
1780 
1781 #ifdef __lock_lint
1782 	(void) mutex_lock(&svc_send_mutex(SVCEXT(xprt)->parent));
1783 #else
1784 	if (svc_mt_mode != RPC_SVC_MT_NONE)
1785 /* LINTED pointer alignment */
1786 		(void) mutex_lock(&svc_send_mutex(SVCEXT(xprt)->parent));
1787 #endif
1788 
1789 	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
1790 				msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
1791 		has_args = TRUE;
1792 		xdr_results = msg->acpted_rply.ar_results.proc;
1793 		xdr_location = msg->acpted_rply.ar_results.where;
1794 		msg->acpted_rply.ar_results.proc = xdr_void;
1795 		msg->acpted_rply.ar_results.where = NULL;
1796 	} else
1797 		has_args = FALSE;
1798 
1799 	xdrs->x_op = XDR_ENCODE;
1800 	msg->rm_xid = cd->x_id;
1801 /* LINTED pointer alignment */
1802 	if (xdr_replymsg(xdrs, msg) && (!has_args || SVCAUTH_WRAP(
1803 			&SVC_XP_AUTH(xprt), xdrs, xdr_results, xdr_location))) {
1804 		stat = TRUE;
1805 	}
1806 	(void) xdrrec_endofrecord(xdrs, TRUE);
1807 
1808 #ifdef __lock_lint
1809 	(void) mutex_unlock(&svc_send_mutex(SVCEXT(xprt)->parent));
1810 #else
1811 	if (svc_mt_mode != RPC_SVC_MT_NONE)
1812 /* LINTED pointer alignment */
1813 		(void) mutex_unlock(&svc_send_mutex(SVCEXT(xprt)->parent));
1814 #endif
1815 
1816 	return (stat);
1817 }
1818 
1819 static struct xp_ops *
1820 svc_vc_ops(void)
1821 {
1822 	static struct xp_ops ops;
1823 	extern mutex_t ops_lock;
1824 
1825 /* VARIABLES PROTECTED BY ops_lock: ops */
1826 
1827 	(void) mutex_lock(&ops_lock);
1828 	if (ops.xp_recv == NULL) {
1829 		ops.xp_recv = svc_vc_recv;
1830 		ops.xp_stat = svc_vc_stat;
1831 		ops.xp_getargs = svc_vc_getargs;
1832 		ops.xp_reply = svc_vc_reply;
1833 		ops.xp_freeargs = svc_vc_freeargs;
1834 		ops.xp_destroy = svc_vc_destroy;
1835 		ops.xp_control = svc_vc_control;
1836 	}
1837 	(void) mutex_unlock(&ops_lock);
1838 	return (&ops);
1839 }
1840 
1841 static struct xp_ops *
1842 svc_vc_rendezvous_ops(void)
1843 {
1844 	static struct xp_ops ops;
1845 	extern mutex_t ops_lock;
1846 
1847 	(void) mutex_lock(&ops_lock);
1848 	if (ops.xp_recv == NULL) {
1849 		ops.xp_recv = rendezvous_request;
1850 		ops.xp_stat = rendezvous_stat;
1851 		ops.xp_getargs = (bool_t (*)())abort;
1852 		ops.xp_reply = (bool_t (*)())abort;
1853 		ops.xp_freeargs = (bool_t (*)())abort,
1854 		ops.xp_destroy = svc_vc_destroy;
1855 		ops.xp_control = rendezvous_control;
1856 	}
1857 	(void) mutex_unlock(&ops_lock);
1858 	return (&ops);
1859 }
1860 
1861 /*
1862  * PRIVATE RPC INTERFACE
1863  *
1864  * This is a hack to let NIS+ clean up connections that have already been
1865  * closed.  This problem arises because rpc.nisd forks a child to handle
1866  * existing connections when it does checkpointing.  The child may close
1867  * some of these connections.  But the descriptors still stay open in the
1868  * parent, and because TLI descriptors don't support persistent EOF
1869  * condition (like sockets do), the parent will never detect that these
1870  * descriptors are dead.
1871  *
1872  * The following internal procedure __svc_nisplus_fdcleanup_hack() - should
1873  * be removed as soon as rpc.nisd is rearchitected to do the right thing.
1874  * This procedure should not find its way into any header files.
1875  *
1876  * This procedure should be called only when rpc.nisd knows that there
1877  * are no children servicing clients.
1878  */
1879 
1880 static bool_t
1881 fd_is_dead(int fd)
1882 {
1883 	struct T_info_ack inforeq;
1884 	int retval;
1885 
1886 	inforeq.PRIM_type = T_INFO_REQ;
1887 	if (!_t_do_ioctl(fd, (caddr_t)&inforeq, sizeof (struct T_info_req),
1888 						TI_GETINFO, &retval))
1889 		return (TRUE);
1890 	if (retval != (int)sizeof (struct T_info_ack))
1891 		return (TRUE);
1892 
1893 	switch (inforeq.CURRENT_state) {
1894 	case TS_UNBND:
1895 	case TS_IDLE:
1896 		return (TRUE);
1897 	default:
1898 		break;
1899 	}
1900 	return (FALSE);
1901 }
1902 
1903 void
1904 __svc_nisplus_fdcleanup_hack(void)
1905 {
1906 	SVCXPRT *xprt;
1907 	SVCXPRT *dead_xprt[CLEANUP_SIZE];
1908 	int i, fd_idx = 0, dead_idx = 0;
1909 
1910 	if (svc_xports == NULL)
1911 		return;
1912 	for (;;) {
1913 		(void) rw_wrlock(&svc_fd_lock);
1914 		for (dead_idx = 0; fd_idx < svc_max_pollfd; fd_idx++) {
1915 			if ((xprt = svc_xports[fd_idx]) == NULL)
1916 				continue;
1917 /* LINTED pointer alignment */
1918 			if (svc_type(xprt) != SVC_CONNECTION)
1919 				continue;
1920 			if (fd_is_dead(fd_idx)) {
1921 				dead_xprt[dead_idx++] = xprt;
1922 				if (dead_idx >= CLEANUP_SIZE)
1923 					break;
1924 			}
1925 		}
1926 
1927 		for (i = 0; i < dead_idx; i++) {
1928 			/* Still holding svc_fd_lock */
1929 			_svc_vc_destroy_private(dead_xprt[i], FALSE);
1930 		}
1931 		(void) rw_unlock(&svc_fd_lock);
1932 		if (fd_idx++ >= svc_max_pollfd)
1933 			return;
1934 	}
1935 }
1936 
1937 void
1938 __svc_nisplus_enable_timestamps(void)
1939 {
1940 	(void) mutex_lock(&timestamp_lock);
1941 	if (!timestamps) {
1942 		timestamps = calloc(FD_INCREMENT, sizeof (long));
1943 		if (timestamps != NULL)
1944 			ntimestamps = FD_INCREMENT;
1945 		else {
1946 			(void) mutex_unlock(&timestamp_lock);
1947 			syslog(LOG_ERR,
1948 				"__svc_nisplus_enable_timestamps: "
1949 				"out of memory");
1950 			return;
1951 		}
1952 	}
1953 	(void) mutex_unlock(&timestamp_lock);
1954 }
1955 
1956 void
1957 __svc_nisplus_purge_since(long since)
1958 {
1959 	SVCXPRT *xprt;
1960 	SVCXPRT *dead_xprt[CLEANUP_SIZE];
1961 	int i, fd_idx = 0, dead_idx = 0;
1962 
1963 	if (svc_xports == NULL)
1964 		return;
1965 	for (;;) {
1966 		(void) rw_wrlock(&svc_fd_lock);
1967 		(void) mutex_lock(&timestamp_lock);
1968 		for (dead_idx = 0; fd_idx < svc_max_pollfd; fd_idx++) {
1969 			if ((xprt = svc_xports[fd_idx]) == NULL) {
1970 				continue;
1971 			}
1972 			/* LINTED pointer cast */
1973 			if (svc_type(xprt) != SVC_CONNECTION) {
1974 				continue;
1975 			}
1976 			if (fd_idx >= ntimestamps) {
1977 				break;
1978 			}
1979 			if (timestamps[fd_idx] &&
1980 			    timestamps[fd_idx] < since) {
1981 				dead_xprt[dead_idx++] = xprt;
1982 				if (dead_idx >= CLEANUP_SIZE)
1983 					break;
1984 			}
1985 		}
1986 		(void) mutex_unlock(&timestamp_lock);
1987 
1988 		for (i = 0; i < dead_idx; i++) {
1989 			/* Still holding svc_fd_lock */
1990 			_svc_vc_destroy_private(dead_xprt[i], FALSE);
1991 		}
1992 		(void) rw_unlock(&svc_fd_lock);
1993 		if (fd_idx++ >= svc_max_pollfd)
1994 			return;
1995 	}
1996 }
1997 
1998 /*
1999  * dup cache wrapper functions for vc requests. The set of dup
2000  * functions were written with the view that they may be expanded
2001  * during creation of a generic svc_vc_enablecache routine
2002  * which would have a size based cache, rather than a time based cache.
2003  * The real work is done in generic svc.c
2004  */
2005 bool_t
2006 __svc_vc_dupcache_init(SVCXPRT *xprt, void *condition, int basis)
2007 {
2008 	return (__svc_dupcache_init(condition, basis,
2009 		/* LINTED pointer alignment */
2010 		&(((struct cf_rendezvous *)xprt->xp_p1)->cf_cache)));
2011 }
2012 
2013 int
2014 __svc_vc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz)
2015 {
2016 	return (__svc_dup(req, resp_buf, resp_bufsz,
2017 		/* LINTED pointer alignment */
2018 		((struct cf_conn *)req->rq_xprt->xp_p1)->cf_cache));
2019 }
2020 
2021 int
2022 __svc_vc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2023 				int status)
2024 {
2025 	return (__svc_dupdone(req, resp_buf, resp_bufsz, status,
2026 		/* LINTED pointer alignment */
2027 		((struct cf_conn *)req->rq_xprt->xp_p1)->cf_cache));
2028 }
2029