xref: /onnv-gate/usr/src/cmd/fs.d/nfs/lib/nfs_tbind.c (revision 7208:568549b138d8)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51676Sjpk  * Common Development and Distribution License (the "License").
61676Sjpk  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7208Svv149972  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * nfs_tbind.c, common part for nfsd and lockd.
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
310Sstevel@tonic-gate 
322712Snn35248 #define	PORTMAP
332712Snn35248 
340Sstevel@tonic-gate #include <tiuser.h>
350Sstevel@tonic-gate #include <fcntl.h>
360Sstevel@tonic-gate #include <netconfig.h>
370Sstevel@tonic-gate #include <stropts.h>
380Sstevel@tonic-gate #include <errno.h>
390Sstevel@tonic-gate #include <syslog.h>
400Sstevel@tonic-gate #include <rpc/rpc.h>
412712Snn35248 #include <rpc/pmap_prot.h>
420Sstevel@tonic-gate #include <sys/time.h>
430Sstevel@tonic-gate #include <sys/resource.h>
440Sstevel@tonic-gate #include <signal.h>
450Sstevel@tonic-gate #include <netdir.h>
460Sstevel@tonic-gate #include <unistd.h>
470Sstevel@tonic-gate #include <string.h>
480Sstevel@tonic-gate #include <netinet/tcp.h>
490Sstevel@tonic-gate #include <malloc.h>
500Sstevel@tonic-gate #include <stdlib.h>
510Sstevel@tonic-gate #include "nfs_tbind.h"
520Sstevel@tonic-gate #include <nfs/nfs.h>
530Sstevel@tonic-gate #include <nfs/nfs_acl.h>
540Sstevel@tonic-gate #include <nfs/nfssys.h>
550Sstevel@tonic-gate #include <nfs/nfs4.h>
561676Sjpk #include <zone.h>
571676Sjpk #include <sys/socket.h>
581676Sjpk #include <tsol/label.h>
590Sstevel@tonic-gate 
600Sstevel@tonic-gate /*
610Sstevel@tonic-gate  * Determine valid semantics for most applications.
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate #define	OK_TPI_TYPE(_nconf) \
640Sstevel@tonic-gate 	(_nconf->nc_semantics == NC_TPI_CLTS || \
650Sstevel@tonic-gate 	_nconf->nc_semantics == NC_TPI_COTS || \
660Sstevel@tonic-gate 	_nconf->nc_semantics == NC_TPI_COTS_ORD)
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #define	BE32_TO_U32(a) \
690Sstevel@tonic-gate 	((((ulong_t)((uchar_t *)a)[0] & 0xFF) << (ulong_t)24) | \
700Sstevel@tonic-gate 	(((ulong_t)((uchar_t *)a)[1] & 0xFF) << (ulong_t)16) | \
710Sstevel@tonic-gate 	(((ulong_t)((uchar_t *)a)[2] & 0xFF) << (ulong_t)8)  | \
720Sstevel@tonic-gate 	((ulong_t)((uchar_t *)a)[3] & 0xFF))
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /*
750Sstevel@tonic-gate  * Number of elements to add to the poll array on each allocation.
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate #define	POLL_ARRAY_INC_SIZE	64
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate  * Number of file descriptors by which the process soft limit may be
810Sstevel@tonic-gate  * increased on each call to nofile_increase(0).
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate #define	NOFILE_INC_SIZE	64
840Sstevel@tonic-gate 
850Sstevel@tonic-gate struct conn_ind {
860Sstevel@tonic-gate 	struct conn_ind *conn_next;
870Sstevel@tonic-gate 	struct conn_ind *conn_prev;
880Sstevel@tonic-gate 	struct t_call   *conn_call;
890Sstevel@tonic-gate };
900Sstevel@tonic-gate 
910Sstevel@tonic-gate struct conn_entry {
920Sstevel@tonic-gate 	bool_t			closing;
930Sstevel@tonic-gate 	struct netconfig	nc;
940Sstevel@tonic-gate };
950Sstevel@tonic-gate 
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate  * this file contains transport routines common to nfsd and lockd
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate static	int	nofile_increase(int);
1000Sstevel@tonic-gate static	int	reuseaddr(int);
1011676Sjpk static	int	recvucred(int);
1021676Sjpk static  int	anonmlp(int);
1030Sstevel@tonic-gate static	void	add_to_poll_list(int, struct netconfig *);
1040Sstevel@tonic-gate static	char	*serv_name_to_port_name(char *);
1050Sstevel@tonic-gate static	int	bind_to_proto(char *, char *, struct netbuf **,
1060Sstevel@tonic-gate 				struct netconfig **);
1070Sstevel@tonic-gate static	int	bind_to_provider(char *, char *, struct netbuf **,
1080Sstevel@tonic-gate 					struct netconfig **);
1090Sstevel@tonic-gate static	void	conn_close_oldest(void);
1100Sstevel@tonic-gate static	boolean_t conn_get(int, struct netconfig *, struct conn_ind **);
1110Sstevel@tonic-gate static	void	cots_listen_event(int, int);
1120Sstevel@tonic-gate static	int	discon_get(int, struct netconfig *, struct conn_ind **);
1130Sstevel@tonic-gate static	int	do_poll_clts_action(int, int);
1140Sstevel@tonic-gate static	int	do_poll_cots_action(int, int);
1150Sstevel@tonic-gate static	void	remove_from_poll_list(int);
1160Sstevel@tonic-gate static	int	set_addrmask(int, struct netconfig *, struct netbuf *);
1170Sstevel@tonic-gate static	int	is_listen_fd_index(int);
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate static	struct pollfd *poll_array;
1200Sstevel@tonic-gate static	struct conn_entry *conn_polled;
1210Sstevel@tonic-gate static	int	num_conns;		/* Current number of connections */
1220Sstevel@tonic-gate int		(*Mysvc4)(int, struct netbuf *, struct netconfig *, int,
1230Sstevel@tonic-gate 		struct netbuf *);
1240Sstevel@tonic-gate 
1252712Snn35248 extern bool_t __pmap_set(const rpcprog_t program, const rpcvers_t version,
1262712Snn35248     const struct netconfig *nconf, const struct netbuf *address);
1272712Snn35248 
1280Sstevel@tonic-gate /*
1290Sstevel@tonic-gate  * Called to create and prepare a transport descriptor for in-kernel
1300Sstevel@tonic-gate  * RPC service.
1310Sstevel@tonic-gate  * Returns -1 on failure and a valid descriptor on success.
1320Sstevel@tonic-gate  */
1330Sstevel@tonic-gate int
1340Sstevel@tonic-gate nfslib_transport_open(struct netconfig *nconf)
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate 	int fd;
1370Sstevel@tonic-gate 	struct strioctl	strioc;
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	if ((nconf == (struct netconfig *)NULL) ||
1400Sstevel@tonic-gate 	    (nconf->nc_device == (char *)NULL)) {
1410Sstevel@tonic-gate 		syslog(LOG_ERR, "no netconfig device");
1420Sstevel@tonic-gate 		return (-1);
1430Sstevel@tonic-gate 	}
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	/*
1460Sstevel@tonic-gate 	 * Open the transport device.
1470Sstevel@tonic-gate 	 */
1480Sstevel@tonic-gate 	fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL);
1490Sstevel@tonic-gate 	if (fd == -1) {
1500Sstevel@tonic-gate 		if (t_errno == TSYSERR && errno == EMFILE &&
1510Sstevel@tonic-gate 		    (nofile_increase(0) == 0)) {
1520Sstevel@tonic-gate 			/* Try again with a higher NOFILE limit. */
1530Sstevel@tonic-gate 			fd = t_open(nconf->nc_device, O_RDWR,
154*7208Svv149972 			    (struct t_info *)NULL);
1550Sstevel@tonic-gate 		}
1560Sstevel@tonic-gate 		if (fd == -1) {
1570Sstevel@tonic-gate 			syslog(LOG_ERR, "t_open %s failed:  t_errno %d, %m",
1580Sstevel@tonic-gate 			    nconf->nc_device, t_errno);
1590Sstevel@tonic-gate 			return (-1);
1600Sstevel@tonic-gate 		}
1610Sstevel@tonic-gate 	}
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	/*
1640Sstevel@tonic-gate 	 * Pop timod because the RPC module must be as close as possible
1650Sstevel@tonic-gate 	 * to the transport.
1660Sstevel@tonic-gate 	 */
1670Sstevel@tonic-gate 	if (ioctl(fd, I_POP, 0) < 0) {
1680Sstevel@tonic-gate 		syslog(LOG_ERR, "I_POP of timod failed: %m");
1690Sstevel@tonic-gate 		(void) t_close(fd);
1700Sstevel@tonic-gate 		return (-1);
1710Sstevel@tonic-gate 	}
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	/*
1740Sstevel@tonic-gate 	 * Common code for CLTS and COTS transports
1750Sstevel@tonic-gate 	 */
1760Sstevel@tonic-gate 	if (ioctl(fd, I_PUSH, "rpcmod") < 0) {
1770Sstevel@tonic-gate 		syslog(LOG_ERR, "I_PUSH of rpcmod failed: %m");
1780Sstevel@tonic-gate 		(void) t_close(fd);
1790Sstevel@tonic-gate 		return (-1);
1800Sstevel@tonic-gate 	}
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	strioc.ic_cmd = RPC_SERVER;
1830Sstevel@tonic-gate 	strioc.ic_dp = (char *)0;
1840Sstevel@tonic-gate 	strioc.ic_len = 0;
1850Sstevel@tonic-gate 	strioc.ic_timout = -1;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	/* Tell rpcmod to act like a server stream. */
1880Sstevel@tonic-gate 	if (ioctl(fd, I_STR, &strioc) < 0) {
1890Sstevel@tonic-gate 		syslog(LOG_ERR, "rpcmod set-up ioctl failed: %m");
1900Sstevel@tonic-gate 		(void) t_close(fd);
1910Sstevel@tonic-gate 		return (-1);
1920Sstevel@tonic-gate 	}
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	/*
1950Sstevel@tonic-gate 	 * Re-push timod so that we will still be doing TLI
1960Sstevel@tonic-gate 	 * operations on the descriptor.
1970Sstevel@tonic-gate 	 */
1980Sstevel@tonic-gate 	if (ioctl(fd, I_PUSH, "timod") < 0) {
1990Sstevel@tonic-gate 		syslog(LOG_ERR, "I_PUSH of timod failed: %m");
2000Sstevel@tonic-gate 		(void) t_close(fd);
2010Sstevel@tonic-gate 		return (-1);
2020Sstevel@tonic-gate 	}
2030Sstevel@tonic-gate 
204*7208Svv149972 	/*
205*7208Svv149972 	 * Enable options of returning the ip's for udp.
206*7208Svv149972 	 */
207*7208Svv149972 	if (strcmp(nconf->nc_netid, "udp6") == 0)
208*7208Svv149972 		__rpc_tli_set_options(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1);
209*7208Svv149972 	else if (strcmp(nconf->nc_netid, "udp") == 0)
210*7208Svv149972 		__rpc_tli_set_options(fd, IPPROTO_IP, IP_RECVDSTADDR, 1);
211*7208Svv149972 
2120Sstevel@tonic-gate 	return (fd);
2130Sstevel@tonic-gate }
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate static int
2160Sstevel@tonic-gate nofile_increase(int limit)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate 	struct rlimit rl;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
2210Sstevel@tonic-gate 		syslog(LOG_ERR, "getrlimit of NOFILE failed: %m");
2220Sstevel@tonic-gate 		return (-1);
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	if (limit > 0)
2260Sstevel@tonic-gate 		rl.rlim_cur = limit;
2270Sstevel@tonic-gate 	else
2280Sstevel@tonic-gate 		rl.rlim_cur += NOFILE_INC_SIZE;
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	if (rl.rlim_cur > rl.rlim_max &&
2310Sstevel@tonic-gate 	    rl.rlim_max != RLIM_INFINITY)
2320Sstevel@tonic-gate 		rl.rlim_max = rl.rlim_cur;
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
2350Sstevel@tonic-gate 		syslog(LOG_ERR, "setrlimit of NOFILE to %d failed: %m",
236*7208Svv149972 		    rl.rlim_cur);
2370Sstevel@tonic-gate 		return (-1);
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	return (0);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate int
2440Sstevel@tonic-gate nfslib_bindit(struct netconfig *nconf, struct netbuf **addr,
2450Sstevel@tonic-gate 	struct nd_hostserv *hs, int backlog)
2460Sstevel@tonic-gate {
2470Sstevel@tonic-gate 	int fd;
2480Sstevel@tonic-gate 	struct t_bind  *ntb;
2490Sstevel@tonic-gate 	struct t_bind tb;
2500Sstevel@tonic-gate 	struct nd_addrlist *addrlist;
2510Sstevel@tonic-gate 	struct t_optmgmt req, resp;
2520Sstevel@tonic-gate 	struct opthdr *opt;
2530Sstevel@tonic-gate 	char reqbuf[128];
2540Sstevel@tonic-gate 	bool_t use_any = FALSE;
2551676Sjpk 	bool_t gzone = TRUE;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	if ((fd = nfslib_transport_open(nconf)) == -1) {
2580Sstevel@tonic-gate 		syslog(LOG_ERR, "cannot establish transport service over %s",
259*7208Svv149972 		    nconf->nc_device);
2600Sstevel@tonic-gate 		return (-1);
2610Sstevel@tonic-gate 	}
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	addrlist = (struct nd_addrlist *)NULL;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	/* nfs4_callback service does not used a fieed port number */
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	if (strcmp(hs->h_serv, "nfs4_callback") == 0) {
2680Sstevel@tonic-gate 		tb.addr.maxlen = 0;
2690Sstevel@tonic-gate 		tb.addr.len = 0;
2700Sstevel@tonic-gate 		tb.addr.buf = 0;
2710Sstevel@tonic-gate 		use_any = TRUE;
2721676Sjpk 		gzone = (getzoneid() == GLOBAL_ZONEID);
2730Sstevel@tonic-gate 	} else if (netdir_getbyname(nconf, hs, &addrlist) != 0) {
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 		syslog(LOG_ERR,
2760Sstevel@tonic-gate 		"Cannot get address for transport %s host %s service %s",
277*7208Svv149972 		    nconf->nc_netid, hs->h_host, hs->h_serv);
2780Sstevel@tonic-gate 		(void) t_close(fd);
2790Sstevel@tonic-gate 		return (-1);
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	if (strcmp(nconf->nc_proto, "tcp") == 0) {
2830Sstevel@tonic-gate 		/*
2840Sstevel@tonic-gate 		 * If we're running over TCP, then set the
2850Sstevel@tonic-gate 		 * SO_REUSEADDR option so that we can bind
2860Sstevel@tonic-gate 		 * to our preferred address even if previously
2870Sstevel@tonic-gate 		 * left connections exist in FIN_WAIT states.
2880Sstevel@tonic-gate 		 * This is somewhat bogus, but otherwise you have
2890Sstevel@tonic-gate 		 * to wait 2 minutes to restart after killing it.
2900Sstevel@tonic-gate 		 */
2910Sstevel@tonic-gate 		if (reuseaddr(fd) == -1) {
2920Sstevel@tonic-gate 			syslog(LOG_WARNING,
2930Sstevel@tonic-gate 			"couldn't set SO_REUSEADDR option on transport");
2940Sstevel@tonic-gate 		}
2951676Sjpk 	} else if (strcmp(nconf->nc_proto, "udp") == 0) {
2961676Sjpk 		/*
2971676Sjpk 		 * In order to run MLP on UDP, we need to handle creds.
2981676Sjpk 		 */
2991676Sjpk 		if (recvucred(fd) == -1) {
3001676Sjpk 			syslog(LOG_WARNING,
3011676Sjpk 			    "couldn't set SO_RECVUCRED option on transport");
3021676Sjpk 		}
3031676Sjpk 	}
3041676Sjpk 
3051676Sjpk 	/*
3061676Sjpk 	 * Make non global zone nfs4_callback port MLP
3071676Sjpk 	 */
3081676Sjpk 	if (use_any && is_system_labeled() && !gzone) {
3091676Sjpk 		if (anonmlp(fd) == -1) {
3101676Sjpk 			/*
3111676Sjpk 			 * failing to set this option means nfs4_callback
3121676Sjpk 			 * could fail silently later. So fail it with
3131676Sjpk 			 * with an error message now.
3141676Sjpk 			 */
3151676Sjpk 			syslog(LOG_ERR,
3161676Sjpk 			    "couldn't set SO_ANON_MLP option on transport");
3171676Sjpk 			(void) t_close(fd);
3181676Sjpk 			return (-1);
3191676Sjpk 		}
3200Sstevel@tonic-gate 	}
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	if (nconf->nc_semantics == NC_TPI_CLTS)
3230Sstevel@tonic-gate 		tb.qlen = 0;
3240Sstevel@tonic-gate 	else
3250Sstevel@tonic-gate 		tb.qlen = backlog;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	/* LINTED pointer alignment */
3280Sstevel@tonic-gate 	ntb = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL);
3290Sstevel@tonic-gate 	if (ntb == (struct t_bind *)NULL) {
3300Sstevel@tonic-gate 		syslog(LOG_ERR, "t_alloc failed:  t_errno %d, %m", t_errno);
3310Sstevel@tonic-gate 		(void) t_close(fd);
3320Sstevel@tonic-gate 		netdir_free((void *)addrlist, ND_ADDRLIST);
3330Sstevel@tonic-gate 		return (-1);
3340Sstevel@tonic-gate 	}
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	/*
3370Sstevel@tonic-gate 	 * XXX - what about the space tb->addr.buf points to? This should
3380Sstevel@tonic-gate 	 * be either a memcpy() to/from the buf fields, or t_alloc(fd,T_BIND,)
3390Sstevel@tonic-gate 	 * should't be called with T_ALL.
3400Sstevel@tonic-gate 	 */
3410Sstevel@tonic-gate 	if (addrlist)
3420Sstevel@tonic-gate 		tb.addr = *(addrlist->n_addrs);		/* structure copy */
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	if (t_bind(fd, &tb, ntb) == -1) {
3450Sstevel@tonic-gate 		syslog(LOG_ERR, "t_bind failed:  t_errno %d, %m", t_errno);
3460Sstevel@tonic-gate 		(void) t_free((char *)ntb, T_BIND);
3470Sstevel@tonic-gate 		netdir_free((void *)addrlist, ND_ADDRLIST);
3480Sstevel@tonic-gate 		(void) t_close(fd);
3490Sstevel@tonic-gate 		return (-1);
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	/* make sure we bound to the right address */
3530Sstevel@tonic-gate 	if (use_any == FALSE &&
3540Sstevel@tonic-gate 	    (tb.addr.len != ntb->addr.len ||
3550Sstevel@tonic-gate 	    memcmp(tb.addr.buf, ntb->addr.buf, tb.addr.len) != 0)) {
3560Sstevel@tonic-gate 		syslog(LOG_ERR, "t_bind to wrong address");
3570Sstevel@tonic-gate 		(void) t_free((char *)ntb, T_BIND);
3580Sstevel@tonic-gate 		netdir_free((void *)addrlist, ND_ADDRLIST);
3590Sstevel@tonic-gate 		(void) t_close(fd);
3600Sstevel@tonic-gate 		return (-1);
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	/*
3640Sstevel@tonic-gate 	 * Call nfs4svc_setport so that the kernel can be
3650Sstevel@tonic-gate 	 * informed what port number the daemon is listing
3660Sstevel@tonic-gate 	 * for incoming connection requests.
3670Sstevel@tonic-gate 	 */
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	if ((nconf->nc_semantics == NC_TPI_COTS ||
3700Sstevel@tonic-gate 	    nconf->nc_semantics == NC_TPI_COTS_ORD) && Mysvc4 != NULL)
3710Sstevel@tonic-gate 		(*Mysvc4)(fd, NULL, nconf, NFS4_SETPORT, &ntb->addr);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	*addr = &ntb->addr;
3740Sstevel@tonic-gate 	netdir_free((void *)addrlist, ND_ADDRLIST);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	if (strcmp(nconf->nc_proto, "tcp") == 0) {
3770Sstevel@tonic-gate 		/*
3780Sstevel@tonic-gate 		 * Disable the Nagle algorithm on TCP connections.
3790Sstevel@tonic-gate 		 * Connections accepted from this listener will
3800Sstevel@tonic-gate 		 * inherit the listener options.
3810Sstevel@tonic-gate 		 */
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 		/* LINTED pointer alignment */
3840Sstevel@tonic-gate 		opt = (struct opthdr *)reqbuf;
3850Sstevel@tonic-gate 		opt->level = IPPROTO_TCP;
3860Sstevel@tonic-gate 		opt->name = TCP_NODELAY;
3870Sstevel@tonic-gate 		opt->len = sizeof (int);
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 		/* LINTED pointer alignment */
3900Sstevel@tonic-gate 		*(int *)((char *)opt + sizeof (*opt)) = 1;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 		req.flags = T_NEGOTIATE;
3930Sstevel@tonic-gate 		req.opt.len = sizeof (*opt) + opt->len;
3940Sstevel@tonic-gate 		req.opt.buf = (char *)opt;
3950Sstevel@tonic-gate 		resp.flags = 0;
3960Sstevel@tonic-gate 		resp.opt.buf = reqbuf;
3970Sstevel@tonic-gate 		resp.opt.maxlen = sizeof (reqbuf);
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 		if (t_optmgmt(fd, &req, &resp) < 0 ||
400*7208Svv149972 		    resp.flags != T_SUCCESS) {
4010Sstevel@tonic-gate 			syslog(LOG_ERR,
4020Sstevel@tonic-gate 	"couldn't set NODELAY option for proto %s: t_errno = %d, %m",
403*7208Svv149972 			    nconf->nc_proto, t_errno);
4040Sstevel@tonic-gate 		}
4050Sstevel@tonic-gate 	}
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	return (fd);
4080Sstevel@tonic-gate }
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate static int
4111676Sjpk setopt(int fd, int level, int name, int value)
4120Sstevel@tonic-gate {
4130Sstevel@tonic-gate 	struct t_optmgmt req, resp;
4141676Sjpk 	struct {
4151676Sjpk 		struct opthdr opt;
4161676Sjpk 		int value;
4171676Sjpk 	} reqbuf;
4180Sstevel@tonic-gate 
4191676Sjpk 	reqbuf.opt.level = level;
4201676Sjpk 	reqbuf.opt.name = name;
4211676Sjpk 	reqbuf.opt.len = sizeof (int);
4220Sstevel@tonic-gate 
4231676Sjpk 	reqbuf.value = value;
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	req.flags = T_NEGOTIATE;
4261676Sjpk 	req.opt.len = sizeof (reqbuf);
4271676Sjpk 	req.opt.buf = (char *)&reqbuf;
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	resp.flags = 0;
4301676Sjpk 	resp.opt.buf = (char *)&reqbuf;
4310Sstevel@tonic-gate 	resp.opt.maxlen = sizeof (reqbuf);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
4340Sstevel@tonic-gate 		t_error("t_optmgmt");
4350Sstevel@tonic-gate 		return (-1);
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 	return (0);
4380Sstevel@tonic-gate }
4390Sstevel@tonic-gate 
4401676Sjpk static int
4411676Sjpk reuseaddr(int fd)
4421676Sjpk {
4431676Sjpk 	return (setopt(fd, SOL_SOCKET, SO_REUSEADDR, 1));
4441676Sjpk }
4451676Sjpk 
4461676Sjpk static int
4471676Sjpk recvucred(int fd)
4481676Sjpk {
4491676Sjpk 	return (setopt(fd, SOL_SOCKET, SO_RECVUCRED, 1));
4501676Sjpk }
4511676Sjpk 
4521676Sjpk static int
4531676Sjpk anonmlp(int fd)
4541676Sjpk {
4551676Sjpk 	return (setopt(fd, SOL_SOCKET, SO_ANON_MLP, 1));
4561676Sjpk }
4571676Sjpk 
4580Sstevel@tonic-gate void
4590Sstevel@tonic-gate nfslib_log_tli_error(char *tli_name, int fd, struct netconfig *nconf)
4600Sstevel@tonic-gate {
4610Sstevel@tonic-gate 	int error;
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	/*
4640Sstevel@tonic-gate 	 * Save the error code across syslog(), just in case syslog()
4650Sstevel@tonic-gate 	 * gets its own error and, therefore, overwrites errno.
4660Sstevel@tonic-gate 	 */
4670Sstevel@tonic-gate 	error = errno;
4680Sstevel@tonic-gate 	if (t_errno == TSYSERR) {
4690Sstevel@tonic-gate 		syslog(LOG_ERR, "%s(file descriptor %d/transport %s) %m",
470*7208Svv149972 		    tli_name, fd, nconf->nc_proto);
4710Sstevel@tonic-gate 	} else {
4720Sstevel@tonic-gate 		syslog(LOG_ERR,
473*7208Svv149972 		    "%s(file descriptor %d/transport %s) TLI error %d",
474*7208Svv149972 		    tli_name, fd, nconf->nc_proto, t_errno);
4750Sstevel@tonic-gate 	}
4760Sstevel@tonic-gate 	errno = error;
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate /*
4800Sstevel@tonic-gate  * Called to set up service over a particular transport.
4810Sstevel@tonic-gate  */
4820Sstevel@tonic-gate void
4830Sstevel@tonic-gate do_one(char *provider, NETSELDECL(proto), struct protob *protobp0,
4842712Snn35248 	int (*svc)(int, struct netbuf, struct netconfig *), int use_pmap)
4850Sstevel@tonic-gate {
4860Sstevel@tonic-gate 	register int sock;
4870Sstevel@tonic-gate 	struct protob *protobp;
4880Sstevel@tonic-gate 	struct netbuf *retaddr;
4890Sstevel@tonic-gate 	struct netconfig *retnconf;
4900Sstevel@tonic-gate 	struct netbuf addrmask;
4910Sstevel@tonic-gate 	int vers;
4920Sstevel@tonic-gate 	int err;
4930Sstevel@tonic-gate 	int l;
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	if (provider)
4960Sstevel@tonic-gate 		sock = bind_to_provider(provider, protobp0->serv, &retaddr,
497*7208Svv149972 		    &retnconf);
4980Sstevel@tonic-gate 	else
4990Sstevel@tonic-gate 		sock = bind_to_proto(proto, protobp0->serv, &retaddr,
500*7208Svv149972 		    &retnconf);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	if (sock == -1) {
5030Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
5040Sstevel@tonic-gate 	"Cannot establish %s service over %s: transport setup problem.",
505*7208Svv149972 		    protobp0->serv, provider ? provider : proto);
5060Sstevel@tonic-gate 		return;
5070Sstevel@tonic-gate 	}
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	if (set_addrmask(sock, retnconf, &addrmask) < 0) {
5100Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
5110Sstevel@tonic-gate 		    "Cannot set address mask for %s", retnconf->nc_netid);
5120Sstevel@tonic-gate 		return;
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	/*
5160Sstevel@tonic-gate 	 * Register all versions of the programs in the protocol block list.
5170Sstevel@tonic-gate 	 */
5180Sstevel@tonic-gate 	l = strlen(NC_UDP);
5190Sstevel@tonic-gate 	for (protobp = protobp0; protobp; protobp = protobp->next) {
5200Sstevel@tonic-gate 		for (vers = protobp->versmin; vers <= protobp->versmax;
521*7208Svv149972 		    vers++) {
5220Sstevel@tonic-gate 			if ((protobp->program == NFS_PROGRAM ||
523*7208Svv149972 			    protobp->program == NFS_ACL_PROGRAM) &&
524*7208Svv149972 			    vers == NFS_V4 &&
525*7208Svv149972 			    strncasecmp(retnconf->nc_proto, NC_UDP, l) == 0)
5260Sstevel@tonic-gate 				continue;
5270Sstevel@tonic-gate 
5282712Snn35248 			if (use_pmap) {
5292712Snn35248 				/*
5302712Snn35248 				 * Note that if we're using a portmapper
5312712Snn35248 				 * instead of rpcbind then we can't do an
5322712Snn35248 				 * unregister operation here.
5332712Snn35248 				 *
5342712Snn35248 				 * The reason is that the portmapper unset
5352712Snn35248 				 * operation removes all the entries for a
5362712Snn35248 				 * given program/version regardelss of
5372712Snn35248 				 * transport protocol.
5382712Snn35248 				 *
5392712Snn35248 				 * The caller of this routine needs to ensure
5402712Snn35248 				 * that __pmap_unset() has been called for all
5412712Snn35248 				 * program/version service pairs they plan
5422712Snn35248 				 * to support before they start registering
5432712Snn35248 				 * each program/version/protocol triplet.
5442712Snn35248 				 */
5452712Snn35248 				(void) __pmap_set(protobp->program, vers,
5462712Snn35248 				    retnconf, retaddr);
5472712Snn35248 			} else {
5482712Snn35248 				(void) rpcb_unset(protobp->program, vers,
5492712Snn35248 				    retnconf);
5502712Snn35248 				(void) rpcb_set(protobp->program, vers,
5512712Snn35248 				    retnconf, retaddr);
5522712Snn35248 			}
5530Sstevel@tonic-gate 		}
5540Sstevel@tonic-gate 	}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	if (retnconf->nc_semantics == NC_TPI_CLTS) {
5570Sstevel@tonic-gate 		/* Don't drop core if supporting module(s) aren't loaded. */
5580Sstevel@tonic-gate 		(void) signal(SIGSYS, SIG_IGN);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 		/*
5610Sstevel@tonic-gate 		 * svc() doesn't block, it returns success or failure.
5620Sstevel@tonic-gate 		 */
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 		if (svc == NULL && Mysvc4 != NULL)
5650Sstevel@tonic-gate 			err = (*Mysvc4)(sock, &addrmask, retnconf,
566*7208Svv149972 			    NFS4_SETPORT|NFS4_KRPC_START, retaddr);
5670Sstevel@tonic-gate 		else
5680Sstevel@tonic-gate 			err = (*svc)(sock, addrmask, retnconf);
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 		if (err < 0) {
5710Sstevel@tonic-gate 			(void) syslog(LOG_ERR,
572*7208Svv149972 			    "Cannot establish %s service over <file desc."
573*7208Svv149972 			    " %d, protocol %s> : %m. Exiting",
574*7208Svv149972 			    protobp0->serv, sock, retnconf->nc_proto);
5750Sstevel@tonic-gate 			exit(1);
5760Sstevel@tonic-gate 		}
5770Sstevel@tonic-gate 	}
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 	/*
5800Sstevel@tonic-gate 	 * We successfully set up the server over this transport.
5810Sstevel@tonic-gate 	 * Add this descriptor to the one being polled on.
5820Sstevel@tonic-gate 	 */
5830Sstevel@tonic-gate 	add_to_poll_list(sock, retnconf);
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate /*
5860Sstevel@tonic-gate  * Set up the NFS service over all the available transports.
5870Sstevel@tonic-gate  * Returns -1 for failure, 0 for success.
5880Sstevel@tonic-gate  */
5890Sstevel@tonic-gate int
5900Sstevel@tonic-gate do_all(struct protob *protobp,
5912712Snn35248 	int (*svc)(int, struct netbuf, struct netconfig *), int use_pmap)
5920Sstevel@tonic-gate {
5930Sstevel@tonic-gate 	struct netconfig *nconf;
5940Sstevel@tonic-gate 	NCONF_HANDLE *nc;
5950Sstevel@tonic-gate 	int l;
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
5980Sstevel@tonic-gate 		syslog(LOG_ERR, "setnetconfig failed: %m");
5990Sstevel@tonic-gate 		return (-1);
6000Sstevel@tonic-gate 	}
6010Sstevel@tonic-gate 	l = strlen(NC_UDP);
6020Sstevel@tonic-gate 	while (nconf = getnetconfig(nc)) {
6030Sstevel@tonic-gate 		if ((nconf->nc_flag & NC_VISIBLE) &&
6040Sstevel@tonic-gate 		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0 &&
6050Sstevel@tonic-gate 		    OK_TPI_TYPE(nconf) &&
6060Sstevel@tonic-gate 		    (protobp->program != NFS4_CALLBACK ||
6070Sstevel@tonic-gate 		    strncasecmp(nconf->nc_proto, NC_UDP, l) != 0))
6080Sstevel@tonic-gate 			do_one(nconf->nc_device, nconf->nc_proto,
609*7208Svv149972 			    protobp, svc, use_pmap);
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 	(void) endnetconfig(nc);
6120Sstevel@tonic-gate 	return (0);
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate /*
6160Sstevel@tonic-gate  * poll on the open transport descriptors for events and errors.
6170Sstevel@tonic-gate  */
6180Sstevel@tonic-gate void
6190Sstevel@tonic-gate poll_for_action(void)
6200Sstevel@tonic-gate {
6210Sstevel@tonic-gate 	int nfds;
6220Sstevel@tonic-gate 	int i;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	/*
6250Sstevel@tonic-gate 	 * Keep polling until all transports have been closed. When this
6260Sstevel@tonic-gate 	 * happens, we return.
6270Sstevel@tonic-gate 	 */
6280Sstevel@tonic-gate 	while ((int)num_fds > 0) {
6290Sstevel@tonic-gate 		nfds = poll(poll_array, num_fds, INFTIM);
6300Sstevel@tonic-gate 		switch (nfds) {
6310Sstevel@tonic-gate 		case 0:
6320Sstevel@tonic-gate 			continue;
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 		case -1:
6350Sstevel@tonic-gate 			/*
6360Sstevel@tonic-gate 			 * Some errors from poll could be
6370Sstevel@tonic-gate 			 * due to temporary conditions, and we try to
6380Sstevel@tonic-gate 			 * be robust in the face of them. Other
6390Sstevel@tonic-gate 			 * errors (should never happen in theory)
6400Sstevel@tonic-gate 			 * are fatal (eg. EINVAL, EFAULT).
6410Sstevel@tonic-gate 			 */
6420Sstevel@tonic-gate 			switch (errno) {
6430Sstevel@tonic-gate 			case EINTR:
644*7208Svv149972 				continue;
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 			case EAGAIN:
6470Sstevel@tonic-gate 			case ENOMEM:
6480Sstevel@tonic-gate 				(void) sleep(10);
6490Sstevel@tonic-gate 				continue;
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 			default:
6520Sstevel@tonic-gate 				(void) syslog(LOG_ERR,
653*7208Svv149972 				    "poll failed: %m. Exiting");
6540Sstevel@tonic-gate 				exit(1);
6550Sstevel@tonic-gate 			}
6560Sstevel@tonic-gate 		default:
6570Sstevel@tonic-gate 			break;
6580Sstevel@tonic-gate 		}
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 		/*
6610Sstevel@tonic-gate 		 * Go through the poll list looking for events.
6620Sstevel@tonic-gate 		 */
6630Sstevel@tonic-gate 		for (i = 0; i < num_fds && nfds > 0; i++) {
6640Sstevel@tonic-gate 			if (poll_array[i].revents) {
6650Sstevel@tonic-gate 				nfds--;
6660Sstevel@tonic-gate 				/*
6670Sstevel@tonic-gate 				 * We have a message, so try to read it.
6680Sstevel@tonic-gate 				 * Record the error return in errno,
6690Sstevel@tonic-gate 				 * so that syslog(LOG_ERR, "...%m")
6700Sstevel@tonic-gate 				 * dumps the corresponding error string.
6710Sstevel@tonic-gate 				 */
6720Sstevel@tonic-gate 				if (conn_polled[i].nc.nc_semantics ==
6730Sstevel@tonic-gate 				    NC_TPI_CLTS) {
6740Sstevel@tonic-gate 					errno = do_poll_clts_action(
675*7208Svv149972 					    poll_array[i].fd, i);
6760Sstevel@tonic-gate 				} else {
6770Sstevel@tonic-gate 					errno = do_poll_cots_action(
678*7208Svv149972 					    poll_array[i].fd, i);
6790Sstevel@tonic-gate 				}
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 				if (errno == 0)
6820Sstevel@tonic-gate 					continue;
6830Sstevel@tonic-gate 				/*
6840Sstevel@tonic-gate 				 * Most returned error codes mean that there is
6850Sstevel@tonic-gate 				 * fatal condition which we can only deal with
6860Sstevel@tonic-gate 				 * by closing the transport.
6870Sstevel@tonic-gate 				 */
6880Sstevel@tonic-gate 				if (errno != EAGAIN && errno != ENOMEM) {
6890Sstevel@tonic-gate 					(void) syslog(LOG_ERR,
6900Sstevel@tonic-gate 		"Error (%m) reading descriptor %d/transport %s. Closing it.",
691*7208Svv149972 					    poll_array[i].fd,
692*7208Svv149972 					    conn_polled[i].nc.nc_proto);
6930Sstevel@tonic-gate 					(void) t_close(poll_array[i].fd);
6940Sstevel@tonic-gate 					remove_from_poll_list(poll_array[i].fd);
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 				} else if (errno == ENOMEM)
6970Sstevel@tonic-gate 					(void) sleep(5);
6980Sstevel@tonic-gate 			}
6990Sstevel@tonic-gate 		}
7000Sstevel@tonic-gate 	}
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	(void) syslog(LOG_ERR,
703*7208Svv149972 	    "All transports have been closed with errors. Exiting.");
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate /*
7070Sstevel@tonic-gate  * Allocate poll/transport array entries for this descriptor.
7080Sstevel@tonic-gate  */
7090Sstevel@tonic-gate static void
7100Sstevel@tonic-gate add_to_poll_list(int fd, struct netconfig *nconf)
7110Sstevel@tonic-gate {
7120Sstevel@tonic-gate 	static int poll_array_size = 0;
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 	/*
7150Sstevel@tonic-gate 	 * If the arrays are full, allocate new ones.
7160Sstevel@tonic-gate 	 */
7170Sstevel@tonic-gate 	if (num_fds == poll_array_size) {
7180Sstevel@tonic-gate 		struct pollfd *tpa;
7190Sstevel@tonic-gate 		struct conn_entry *tnp;
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 		if (poll_array_size != 0) {
7220Sstevel@tonic-gate 			tpa = poll_array;
7230Sstevel@tonic-gate 			tnp = conn_polled;
7240Sstevel@tonic-gate 		} else
7250Sstevel@tonic-gate 			tpa = (struct pollfd *)0;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 		poll_array_size += POLL_ARRAY_INC_SIZE;
7280Sstevel@tonic-gate 		/*
7290Sstevel@tonic-gate 		 * Allocate new arrays.
7300Sstevel@tonic-gate 		 */
7310Sstevel@tonic-gate 		poll_array = (struct pollfd *)
7320Sstevel@tonic-gate 		    malloc(poll_array_size * sizeof (struct pollfd) + 256);
7330Sstevel@tonic-gate 		conn_polled = (struct conn_entry *)
7340Sstevel@tonic-gate 		    malloc(poll_array_size * sizeof (struct conn_entry) + 256);
7350Sstevel@tonic-gate 		if (poll_array == (struct pollfd *)NULL ||
7360Sstevel@tonic-gate 		    conn_polled == (struct conn_entry *)NULL) {
7370Sstevel@tonic-gate 			syslog(LOG_ERR, "malloc failed for poll array");
7380Sstevel@tonic-gate 			exit(1);
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 		/*
7420Sstevel@tonic-gate 		 * Copy the data of the old ones into new arrays, and
7430Sstevel@tonic-gate 		 * free the old ones.
7440Sstevel@tonic-gate 		 */
7450Sstevel@tonic-gate 		if (tpa) {
7460Sstevel@tonic-gate 			(void) memcpy((void *)poll_array, (void *)tpa,
747*7208Svv149972 			    num_fds * sizeof (struct pollfd));
7480Sstevel@tonic-gate 			(void) memcpy((void *)conn_polled, (void *)tnp,
749*7208Svv149972 			    num_fds * sizeof (struct conn_entry));
7500Sstevel@tonic-gate 			free((void *)tpa);
7510Sstevel@tonic-gate 			free((void *)tnp);
7520Sstevel@tonic-gate 		}
7530Sstevel@tonic-gate 	}
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	/*
7560Sstevel@tonic-gate 	 * Set the descriptor and event list. All possible events are
7570Sstevel@tonic-gate 	 * polled for.
7580Sstevel@tonic-gate 	 */
7590Sstevel@tonic-gate 	poll_array[num_fds].fd = fd;
7600Sstevel@tonic-gate 	poll_array[num_fds].events = POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI;
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	/*
7630Sstevel@tonic-gate 	 * Copy the transport data over too.
7640Sstevel@tonic-gate 	 */
7650Sstevel@tonic-gate 	conn_polled[num_fds].nc = *nconf;
7660Sstevel@tonic-gate 	conn_polled[num_fds].closing = 0;
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 	/*
7690Sstevel@tonic-gate 	 * Set the descriptor to non-blocking. Avoids a race
7700Sstevel@tonic-gate 	 * between data arriving on the stream and then having it
7710Sstevel@tonic-gate 	 * flushed before we can read it.
7720Sstevel@tonic-gate 	 */
7730Sstevel@tonic-gate 	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
7740Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
7750Sstevel@tonic-gate 	"fcntl(file desc. %d/transport %s, F_SETFL, O_NONBLOCK): %m. Exiting",
776*7208Svv149972 		    num_fds, nconf->nc_proto);
7770Sstevel@tonic-gate 		exit(1);
7780Sstevel@tonic-gate 	}
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate 	/*
7810Sstevel@tonic-gate 	 * Count this descriptor.
7820Sstevel@tonic-gate 	 */
7830Sstevel@tonic-gate 	++num_fds;
7840Sstevel@tonic-gate }
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate static void
7870Sstevel@tonic-gate remove_from_poll_list(int fd)
7880Sstevel@tonic-gate {
7890Sstevel@tonic-gate 	int i;
7900Sstevel@tonic-gate 	int num_to_copy;
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	for (i = 0; i < num_fds; i++) {
7930Sstevel@tonic-gate 		if (poll_array[i].fd == fd) {
7940Sstevel@tonic-gate 			--num_fds;
7950Sstevel@tonic-gate 			num_to_copy = num_fds - i;
7960Sstevel@tonic-gate 			(void) memcpy((void *)&poll_array[i],
797*7208Svv149972 			    (void *)&poll_array[i+1],
798*7208Svv149972 			    num_to_copy * sizeof (struct pollfd));
7990Sstevel@tonic-gate 			(void) memset((void *)&poll_array[num_fds], 0,
800*7208Svv149972 			    sizeof (struct pollfd));
8010Sstevel@tonic-gate 			(void) memcpy((void *)&conn_polled[i],
802*7208Svv149972 			    (void *)&conn_polled[i+1],
803*7208Svv149972 			    num_to_copy * sizeof (struct conn_entry));
8040Sstevel@tonic-gate 			(void) memset((void *)&conn_polled[num_fds], 0,
805*7208Svv149972 			    sizeof (struct conn_entry));
8060Sstevel@tonic-gate 			return;
8070Sstevel@tonic-gate 		}
8080Sstevel@tonic-gate 	}
8090Sstevel@tonic-gate 	syslog(LOG_ERR, "attempt to remove nonexistent fd from poll list");
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate }
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate /*
8140Sstevel@tonic-gate  * Called to read and interpret the event on a connectionless descriptor.
8150Sstevel@tonic-gate  * Returns 0 if successful, or a UNIX error code if failure.
8160Sstevel@tonic-gate  */
8170Sstevel@tonic-gate static int
8180Sstevel@tonic-gate do_poll_clts_action(int fd, int conn_index)
8190Sstevel@tonic-gate {
8200Sstevel@tonic-gate 	int error;
8210Sstevel@tonic-gate 	int ret;
8220Sstevel@tonic-gate 	int flags;
8230Sstevel@tonic-gate 	struct netconfig *nconf = &conn_polled[conn_index].nc;
8240Sstevel@tonic-gate 	static struct t_unitdata *unitdata = NULL;
8250Sstevel@tonic-gate 	static struct t_uderr *uderr = NULL;
8260Sstevel@tonic-gate 	static int oldfd = -1;
8270Sstevel@tonic-gate 	struct nd_hostservlist *host = NULL;
8280Sstevel@tonic-gate 	struct strbuf ctl[1], data[1];
8290Sstevel@tonic-gate 	/*
8300Sstevel@tonic-gate 	 * We just need to have some space to consume the
8310Sstevel@tonic-gate 	 * message in the event we can't use the TLI interface to do the
8320Sstevel@tonic-gate 	 * job.
8330Sstevel@tonic-gate 	 *
8340Sstevel@tonic-gate 	 * We flush the message using getmsg(). For the control part
8350Sstevel@tonic-gate 	 * we allocate enough for any TPI header plus 32 bytes for address
8360Sstevel@tonic-gate 	 * and options. For the data part, there is nothing magic about
8370Sstevel@tonic-gate 	 * the size of the array, but 256 bytes is probably better than
8380Sstevel@tonic-gate 	 * 1 byte, and we don't expect any data portion anyway.
8390Sstevel@tonic-gate 	 *
8400Sstevel@tonic-gate 	 * If the array sizes are too small, we handle this because getmsg()
8410Sstevel@tonic-gate 	 * (called to consume the message) will return MOREDATA|MORECTL.
8420Sstevel@tonic-gate 	 * Thus we just call getmsg() until it's read the message.
8430Sstevel@tonic-gate 	 */
8440Sstevel@tonic-gate 	char ctlbuf[sizeof (union T_primitives) + 32];
8450Sstevel@tonic-gate 	char databuf[256];
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	/*
8480Sstevel@tonic-gate 	 * If this is the same descriptor as the last time
8490Sstevel@tonic-gate 	 * do_poll_clts_action was called, we can save some
8500Sstevel@tonic-gate 	 * de-allocation and allocation.
8510Sstevel@tonic-gate 	 */
8520Sstevel@tonic-gate 	if (oldfd != fd) {
8530Sstevel@tonic-gate 		oldfd = fd;
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 		if (unitdata) {
8560Sstevel@tonic-gate 			(void) t_free((char *)unitdata, T_UNITDATA);
8570Sstevel@tonic-gate 			unitdata = NULL;
8580Sstevel@tonic-gate 		}
8590Sstevel@tonic-gate 		if (uderr) {
8600Sstevel@tonic-gate 			(void) t_free((char *)uderr, T_UDERROR);
8610Sstevel@tonic-gate 			uderr = NULL;
8620Sstevel@tonic-gate 		}
8630Sstevel@tonic-gate 	}
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 	/*
8660Sstevel@tonic-gate 	 * Allocate a unitdata structure for receiving the event.
8670Sstevel@tonic-gate 	 */
8680Sstevel@tonic-gate 	if (unitdata == NULL) {
8690Sstevel@tonic-gate 		/* LINTED pointer alignment */
8700Sstevel@tonic-gate 		unitdata = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL);
8710Sstevel@tonic-gate 		if (unitdata == NULL) {
8720Sstevel@tonic-gate 			if (t_errno == TSYSERR) {
8730Sstevel@tonic-gate 				/*
8740Sstevel@tonic-gate 				 * Save the error code across
8750Sstevel@tonic-gate 				 * syslog(), just in case
8760Sstevel@tonic-gate 				 * syslog() gets its own error
8770Sstevel@tonic-gate 				 * and therefore overwrites errno.
8780Sstevel@tonic-gate 				 */
8790Sstevel@tonic-gate 				error = errno;
8800Sstevel@tonic-gate 				(void) syslog(LOG_ERR,
8810Sstevel@tonic-gate 	"t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed: %m",
882*7208Svv149972 				    fd, nconf->nc_proto);
8830Sstevel@tonic-gate 				return (error);
8840Sstevel@tonic-gate 			}
8850Sstevel@tonic-gate 			(void) syslog(LOG_ERR,
8860Sstevel@tonic-gate "t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed TLI error %d",
887*7208Svv149972 			    fd, nconf->nc_proto, t_errno);
8880Sstevel@tonic-gate 			goto flush_it;
8890Sstevel@tonic-gate 		}
8900Sstevel@tonic-gate 	}
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate try_again:
8930Sstevel@tonic-gate 	flags = 0;
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	/*
8960Sstevel@tonic-gate 	 * The idea is we wait for T_UNITDATA_IND's. Of course,
8970Sstevel@tonic-gate 	 * we don't get any, because rpcmod filters them out.
8980Sstevel@tonic-gate 	 * However, we need to call t_rcvudata() to let TLI
8990Sstevel@tonic-gate 	 * tell us we have a T_UDERROR_IND.
9000Sstevel@tonic-gate 	 *
9010Sstevel@tonic-gate 	 * algorithm is:
9020Sstevel@tonic-gate 	 * 	t_rcvudata(), expecting TLOOK.
9030Sstevel@tonic-gate 	 * 	t_look(), expecting T_UDERR.
9040Sstevel@tonic-gate 	 * 	t_rcvuderr(), expecting success (0).
9050Sstevel@tonic-gate 	 * 	expand destination address into ASCII,
9060Sstevel@tonic-gate 	 *	and dump it.
9070Sstevel@tonic-gate 	 */
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	ret = t_rcvudata(fd, unitdata, &flags);
9100Sstevel@tonic-gate 	if (ret == 0 || t_errno == TBUFOVFLW) {
9110Sstevel@tonic-gate 		(void) syslog(LOG_WARNING,
9120Sstevel@tonic-gate "t_rcvudata(file descriptor %d/transport %s) got unexpected data, %d bytes",
913*7208Svv149972 		    fd, nconf->nc_proto, unitdata->udata.len);
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 		/*
9160Sstevel@tonic-gate 		 * Even though we don't expect any data, in case we do,
9170Sstevel@tonic-gate 		 * keep reading until there is no more.
9180Sstevel@tonic-gate 		 */
9190Sstevel@tonic-gate 		if (flags & T_MORE)
9200Sstevel@tonic-gate 			goto try_again;
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 		return (0);
9230Sstevel@tonic-gate 	}
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	switch (t_errno) {
9260Sstevel@tonic-gate 	case TNODATA:
9270Sstevel@tonic-gate 		return (0);
9280Sstevel@tonic-gate 	case TSYSERR:
9290Sstevel@tonic-gate 		/*
9300Sstevel@tonic-gate 		 * System errors are returned to caller.
9310Sstevel@tonic-gate 		 * Save the error code across
9320Sstevel@tonic-gate 		 * syslog(), just in case
9330Sstevel@tonic-gate 		 * syslog() gets its own error
9340Sstevel@tonic-gate 		 * and therefore overwrites errno.
9350Sstevel@tonic-gate 		 */
9360Sstevel@tonic-gate 		error = errno;
9370Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
938*7208Svv149972 		    "t_rcvudata(file descriptor %d/transport %s) %m",
939*7208Svv149972 		    fd, nconf->nc_proto);
9400Sstevel@tonic-gate 		return (error);
9410Sstevel@tonic-gate 	case TLOOK:
9420Sstevel@tonic-gate 		break;
9430Sstevel@tonic-gate 	default:
9440Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
9450Sstevel@tonic-gate 		"t_rcvudata(file descriptor %d/transport %s) TLI error %d",
946*7208Svv149972 		    fd, nconf->nc_proto, t_errno);
9470Sstevel@tonic-gate 		goto flush_it;
9480Sstevel@tonic-gate 	}
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 	ret = t_look(fd);
9510Sstevel@tonic-gate 	switch (ret) {
9520Sstevel@tonic-gate 	case 0:
9530Sstevel@tonic-gate 		return (0);
9540Sstevel@tonic-gate 	case -1:
9550Sstevel@tonic-gate 		/*
9560Sstevel@tonic-gate 		 * System errors are returned to caller.
9570Sstevel@tonic-gate 		 */
9580Sstevel@tonic-gate 		if (t_errno == TSYSERR) {
9590Sstevel@tonic-gate 			/*
9600Sstevel@tonic-gate 			 * Save the error code across
9610Sstevel@tonic-gate 			 * syslog(), just in case
9620Sstevel@tonic-gate 			 * syslog() gets its own error
9630Sstevel@tonic-gate 			 * and therefore overwrites errno.
9640Sstevel@tonic-gate 			 */
9650Sstevel@tonic-gate 			error = errno;
9660Sstevel@tonic-gate 			(void) syslog(LOG_ERR,
967*7208Svv149972 			    "t_look(file descriptor %d/transport %s) %m",
968*7208Svv149972 			    fd, nconf->nc_proto);
9690Sstevel@tonic-gate 			return (error);
9700Sstevel@tonic-gate 		}
9710Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
972*7208Svv149972 		    "t_look(file descriptor %d/transport %s) TLI error %d",
973*7208Svv149972 		    fd, nconf->nc_proto, t_errno);
9740Sstevel@tonic-gate 		goto flush_it;
9750Sstevel@tonic-gate 	case T_UDERR:
9760Sstevel@tonic-gate 		break;
9770Sstevel@tonic-gate 	default:
9780Sstevel@tonic-gate 		(void) syslog(LOG_WARNING,
9790Sstevel@tonic-gate 	"t_look(file descriptor %d/transport %s) returned %d not T_UDERR (%d)",
980*7208Svv149972 		    fd, nconf->nc_proto, ret, T_UDERR);
9810Sstevel@tonic-gate 	}
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	if (uderr == NULL) {
9840Sstevel@tonic-gate 		/* LINTED pointer alignment */
9850Sstevel@tonic-gate 		uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL);
9860Sstevel@tonic-gate 		if (uderr == NULL) {
9870Sstevel@tonic-gate 			if (t_errno == TSYSERR) {
9880Sstevel@tonic-gate 				/*
9890Sstevel@tonic-gate 				 * Save the error code across
9900Sstevel@tonic-gate 				 * syslog(), just in case
9910Sstevel@tonic-gate 				 * syslog() gets its own error
9920Sstevel@tonic-gate 				 * and therefore overwrites errno.
9930Sstevel@tonic-gate 				 */
9940Sstevel@tonic-gate 				error = errno;
9950Sstevel@tonic-gate 				(void) syslog(LOG_ERR,
9960Sstevel@tonic-gate 	"t_alloc(file descriptor %d/transport %s, T_UDERROR) failed: %m",
997*7208Svv149972 				    fd, nconf->nc_proto);
9980Sstevel@tonic-gate 				return (error);
9990Sstevel@tonic-gate 			}
10000Sstevel@tonic-gate 			(void) syslog(LOG_ERR,
10010Sstevel@tonic-gate "t_alloc(file descriptor %d/transport %s, T_UDERROR) failed TLI error: %d",
1002*7208Svv149972 			    fd, nconf->nc_proto, t_errno);
10030Sstevel@tonic-gate 			goto flush_it;
10040Sstevel@tonic-gate 		}
10050Sstevel@tonic-gate 	}
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	ret = t_rcvuderr(fd, uderr);
10080Sstevel@tonic-gate 	if (ret == 0) {
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 		/*
10110Sstevel@tonic-gate 		 * Save the datagram error in errno, so that the
10120Sstevel@tonic-gate 		 * %m argument to syslog picks up the error string.
10130Sstevel@tonic-gate 		 */
10140Sstevel@tonic-gate 		errno = uderr->error;
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 		/*
10170Sstevel@tonic-gate 		 * Log the datagram error, then log the host that
10180Sstevel@tonic-gate 		 * probably triggerred. Cannot log both in the
10190Sstevel@tonic-gate 		 * same transaction because of packet size limitations
10200Sstevel@tonic-gate 		 * in /dev/log.
10210Sstevel@tonic-gate 		 */
10220Sstevel@tonic-gate 		(void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
10230Sstevel@tonic-gate "NFS response over <file descriptor %d/transport %s> generated error: %m",
1024*7208Svv149972 		    fd, nconf->nc_proto);
10250Sstevel@tonic-gate 
10260Sstevel@tonic-gate 		/*
10270Sstevel@tonic-gate 		 * Try to map the client's address back to a
10280Sstevel@tonic-gate 		 * name.
10290Sstevel@tonic-gate 		 */
10300Sstevel@tonic-gate 		ret = netdir_getbyaddr(nconf, &host, &uderr->addr);
10310Sstevel@tonic-gate 		if (ret != -1 && host && host->h_cnt > 0 &&
10320Sstevel@tonic-gate 		    host->h_hostservs) {
10330Sstevel@tonic-gate 		(void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
10340Sstevel@tonic-gate "Bad NFS response was sent to client with host name: %s; service port: %s",
1035*7208Svv149972 		    host->h_hostservs->h_host,
1036*7208Svv149972 		    host->h_hostservs->h_serv);
10370Sstevel@tonic-gate 		} else {
10380Sstevel@tonic-gate 			int i, j;
10390Sstevel@tonic-gate 			char *buf;
10400Sstevel@tonic-gate 			char *hex = "0123456789abcdef";
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 			/*
10430Sstevel@tonic-gate 			 * Mapping failed, print the whole thing
10440Sstevel@tonic-gate 			 * in ASCII hex.
10450Sstevel@tonic-gate 			 */
10460Sstevel@tonic-gate 			buf = (char *)malloc(uderr->addr.len * 2 + 1);
10470Sstevel@tonic-gate 			for (i = 0, j = 0; i < uderr->addr.len; i++, j += 2) {
10480Sstevel@tonic-gate 				buf[j] = hex[((uderr->addr.buf[i]) >> 4) & 0xf];
10490Sstevel@tonic-gate 				buf[j+1] = hex[uderr->addr.buf[i] & 0xf];
10500Sstevel@tonic-gate 			}
10510Sstevel@tonic-gate 			buf[j] = '\0';
10520Sstevel@tonic-gate 		(void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
10530Sstevel@tonic-gate 	"Bad NFS response was sent to client with transport address: 0x%s",
1054*7208Svv149972 		    buf);
10550Sstevel@tonic-gate 			free((void *)buf);
10560Sstevel@tonic-gate 		}
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 		if (ret == 0 && host != NULL)
10590Sstevel@tonic-gate 			netdir_free((void *)host, ND_HOSTSERVLIST);
10600Sstevel@tonic-gate 		return (0);
10610Sstevel@tonic-gate 	}
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	switch (t_errno) {
10640Sstevel@tonic-gate 	case TNOUDERR:
10650Sstevel@tonic-gate 		goto flush_it;
10660Sstevel@tonic-gate 	case TSYSERR:
10670Sstevel@tonic-gate 		/*
10680Sstevel@tonic-gate 		 * System errors are returned to caller.
10690Sstevel@tonic-gate 		 * Save the error code across
10700Sstevel@tonic-gate 		 * syslog(), just in case
10710Sstevel@tonic-gate 		 * syslog() gets its own error
10720Sstevel@tonic-gate 		 * and therefore overwrites errno.
10730Sstevel@tonic-gate 		 */
10740Sstevel@tonic-gate 		error = errno;
10750Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
1076*7208Svv149972 		    "t_rcvuderr(file descriptor %d/transport %s) %m",
1077*7208Svv149972 		    fd, nconf->nc_proto);
10780Sstevel@tonic-gate 		return (error);
10790Sstevel@tonic-gate 	default:
10800Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
10810Sstevel@tonic-gate 		"t_rcvuderr(file descriptor %d/transport %s) TLI error %d",
1082*7208Svv149972 		    fd, nconf->nc_proto, t_errno);
10830Sstevel@tonic-gate 		goto flush_it;
10840Sstevel@tonic-gate 	}
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate flush_it:
10870Sstevel@tonic-gate 	/*
10880Sstevel@tonic-gate 	 * If we get here, then we could not cope with whatever message
10890Sstevel@tonic-gate 	 * we attempted to read, so flush it. If we did read a message,
10900Sstevel@tonic-gate 	 * and one isn't present, that is all right, because fd is in
10910Sstevel@tonic-gate 	 * nonblocking mode.
10920Sstevel@tonic-gate 	 */
10930Sstevel@tonic-gate 	(void) syslog(LOG_ERR,
10940Sstevel@tonic-gate 	"Flushing one input message from <file descriptor %d/transport %s>",
1095*7208Svv149972 	    fd, nconf->nc_proto);
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 	/*
10980Sstevel@tonic-gate 	 * Read and discard the message. Do this this until there is
10990Sstevel@tonic-gate 	 * no more control/data in the message or until we get an error.
11000Sstevel@tonic-gate 	 */
11010Sstevel@tonic-gate 	do {
11020Sstevel@tonic-gate 		ctl->maxlen = sizeof (ctlbuf);
11030Sstevel@tonic-gate 		ctl->buf = ctlbuf;
11040Sstevel@tonic-gate 		data->maxlen = sizeof (databuf);
11050Sstevel@tonic-gate 		data->buf = databuf;
11060Sstevel@tonic-gate 		flags = 0;
11070Sstevel@tonic-gate 		ret = getmsg(fd, ctl, data, &flags);
11080Sstevel@tonic-gate 		if (ret == -1)
11090Sstevel@tonic-gate 			return (errno);
11100Sstevel@tonic-gate 	} while (ret != 0);
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate 	return (0);
11130Sstevel@tonic-gate }
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate static void
11160Sstevel@tonic-gate conn_close_oldest(void)
11170Sstevel@tonic-gate {
11180Sstevel@tonic-gate 	int fd;
11190Sstevel@tonic-gate 	int i1;
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate 	/*
11220Sstevel@tonic-gate 	 * Find the oldest connection that is not already in the
11230Sstevel@tonic-gate 	 * process of shutting down.
11240Sstevel@tonic-gate 	 */
11250Sstevel@tonic-gate 	for (i1 = end_listen_fds; /* no conditional expression */; i1++) {
11260Sstevel@tonic-gate 		if (i1 >= num_fds)
11270Sstevel@tonic-gate 			return;
11280Sstevel@tonic-gate 		if (conn_polled[i1].closing == 0)
11290Sstevel@tonic-gate 			break;
11300Sstevel@tonic-gate 	}
11310Sstevel@tonic-gate #ifdef DEBUG
11320Sstevel@tonic-gate 	printf("too many connections (%d), releasing oldest (%d)\n",
1133*7208Svv149972 	    num_conns, poll_array[i1].fd);
11340Sstevel@tonic-gate #else
11350Sstevel@tonic-gate 	syslog(LOG_WARNING, "too many connections (%d), releasing oldest (%d)",
1136*7208Svv149972 	    num_conns, poll_array[i1].fd);
11370Sstevel@tonic-gate #endif
11380Sstevel@tonic-gate 	fd = poll_array[i1].fd;
11390Sstevel@tonic-gate 	if (conn_polled[i1].nc.nc_semantics == NC_TPI_COTS) {
11400Sstevel@tonic-gate 		/*
11410Sstevel@tonic-gate 		 * For politeness, send a T_DISCON_REQ to the transport
11420Sstevel@tonic-gate 		 * provider.  We close the stream anyway.
11430Sstevel@tonic-gate 		 */
11440Sstevel@tonic-gate 		(void) t_snddis(fd, (struct t_call *)0);
11450Sstevel@tonic-gate 		num_conns--;
11460Sstevel@tonic-gate 		remove_from_poll_list(fd);
11470Sstevel@tonic-gate 		(void) t_close(fd);
11480Sstevel@tonic-gate 	} else {
11490Sstevel@tonic-gate 		/*
11500Sstevel@tonic-gate 		 * For orderly release, we do not close the stream
11510Sstevel@tonic-gate 		 * until the T_ORDREL_IND arrives to complete
11520Sstevel@tonic-gate 		 * the handshake.
11530Sstevel@tonic-gate 		 */
11540Sstevel@tonic-gate 		if (t_sndrel(fd) == 0)
11550Sstevel@tonic-gate 			conn_polled[i1].closing = 1;
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate }
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate static boolean_t
11600Sstevel@tonic-gate conn_get(int fd, struct netconfig *nconf, struct conn_ind **connp)
11610Sstevel@tonic-gate {
11620Sstevel@tonic-gate 	struct conn_ind	*conn;
11630Sstevel@tonic-gate 	struct conn_ind	*next_conn;
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	conn = (struct conn_ind *)malloc(sizeof (*conn));
11660Sstevel@tonic-gate 	if (conn == NULL) {
11670Sstevel@tonic-gate 		syslog(LOG_ERR, "malloc for listen indication failed");
11680Sstevel@tonic-gate 		return (FALSE);
11690Sstevel@tonic-gate 	}
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 	/* LINTED pointer alignment */
11720Sstevel@tonic-gate 	conn->conn_call = (struct t_call *)t_alloc(fd, T_CALL, T_ALL);
11730Sstevel@tonic-gate 	if (conn->conn_call == NULL) {
11740Sstevel@tonic-gate 		free((char *)conn);
11750Sstevel@tonic-gate 		nfslib_log_tli_error("t_alloc", fd, nconf);
11760Sstevel@tonic-gate 		return (FALSE);
11770Sstevel@tonic-gate 	}
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 	if (t_listen(fd, conn->conn_call) == -1) {
11800Sstevel@tonic-gate 		nfslib_log_tli_error("t_listen", fd, nconf);
11810Sstevel@tonic-gate 		(void) t_free((char *)conn->conn_call, T_CALL);
11820Sstevel@tonic-gate 		free((char *)conn);
11830Sstevel@tonic-gate 		return (FALSE);
11840Sstevel@tonic-gate 	}
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 	if (conn->conn_call->udata.len > 0) {
11870Sstevel@tonic-gate 		syslog(LOG_WARNING,
11880Sstevel@tonic-gate 	"rejecting inbound connection(%s) with %d bytes of connect data",
1189*7208Svv149972 		    nconf->nc_proto, conn->conn_call->udata.len);
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 		conn->conn_call->udata.len = 0;
11920Sstevel@tonic-gate 		(void) t_snddis(fd, conn->conn_call);
11930Sstevel@tonic-gate 		(void) t_free((char *)conn->conn_call, T_CALL);
11940Sstevel@tonic-gate 		free((char *)conn);
11950Sstevel@tonic-gate 		return (FALSE);
11960Sstevel@tonic-gate 	}
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate 	if ((next_conn = *connp) != NULL) {
11990Sstevel@tonic-gate 		next_conn->conn_prev->conn_next = conn;
12000Sstevel@tonic-gate 		conn->conn_next = next_conn;
12010Sstevel@tonic-gate 		conn->conn_prev = next_conn->conn_prev;
12020Sstevel@tonic-gate 		next_conn->conn_prev = conn;
12030Sstevel@tonic-gate 	} else {
12040Sstevel@tonic-gate 		conn->conn_next = conn;
12050Sstevel@tonic-gate 		conn->conn_prev = conn;
12060Sstevel@tonic-gate 		*connp = conn;
12070Sstevel@tonic-gate 	}
12080Sstevel@tonic-gate 	return (TRUE);
12090Sstevel@tonic-gate }
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate static int
12120Sstevel@tonic-gate discon_get(int fd, struct netconfig *nconf, struct conn_ind **connp)
12130Sstevel@tonic-gate {
12140Sstevel@tonic-gate 	struct conn_ind	*conn;
12150Sstevel@tonic-gate 	struct t_discon	discon;
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	discon.udata.buf = (char *)0;
12180Sstevel@tonic-gate 	discon.udata.maxlen = 0;
12190Sstevel@tonic-gate 	if (t_rcvdis(fd, &discon) == -1) {
12200Sstevel@tonic-gate 		nfslib_log_tli_error("t_rcvdis", fd, nconf);
12210Sstevel@tonic-gate 		return (-1);
12220Sstevel@tonic-gate 	}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 	conn = *connp;
12250Sstevel@tonic-gate 	if (conn == NULL)
12260Sstevel@tonic-gate 		return (0);
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 	do {
12290Sstevel@tonic-gate 		if (conn->conn_call->sequence == discon.sequence) {
12300Sstevel@tonic-gate 			if (conn->conn_next == conn)
12310Sstevel@tonic-gate 				*connp = (struct conn_ind *)0;
12320Sstevel@tonic-gate 			else {
12330Sstevel@tonic-gate 				if (conn == *connp) {
12340Sstevel@tonic-gate 					*connp = conn->conn_next;
12350Sstevel@tonic-gate 				}
12360Sstevel@tonic-gate 				conn->conn_next->conn_prev = conn->conn_prev;
12370Sstevel@tonic-gate 				conn->conn_prev->conn_next = conn->conn_next;
12380Sstevel@tonic-gate 			}
12390Sstevel@tonic-gate 			free((char *)conn);
12400Sstevel@tonic-gate 			break;
12410Sstevel@tonic-gate 		}
12420Sstevel@tonic-gate 		conn = conn->conn_next;
12430Sstevel@tonic-gate 	} while (conn != *connp);
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	return (0);
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate static void
12490Sstevel@tonic-gate cots_listen_event(int fd, int conn_index)
12500Sstevel@tonic-gate {
12510Sstevel@tonic-gate 	struct t_call *call;
12520Sstevel@tonic-gate 	struct conn_ind	*conn;
12530Sstevel@tonic-gate 	struct conn_ind	*conn_head;
12540Sstevel@tonic-gate 	int event;
12550Sstevel@tonic-gate 	struct netconfig *nconf = &conn_polled[conn_index].nc;
12560Sstevel@tonic-gate 	int new_fd;
12570Sstevel@tonic-gate 	struct netbuf addrmask;
12580Sstevel@tonic-gate 	int ret = 0;
12590Sstevel@tonic-gate 	char *clnt;
12600Sstevel@tonic-gate 	char *clnt_uaddr = NULL;
12610Sstevel@tonic-gate 	struct nd_hostservlist *clnt_serv = NULL;
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	conn_head = (struct conn_ind *)0;
12640Sstevel@tonic-gate 	(void) conn_get(fd, nconf, &conn_head);
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	while ((conn = conn_head) != NULL) {
12670Sstevel@tonic-gate 		conn_head = conn->conn_next;
12680Sstevel@tonic-gate 		if (conn_head == conn)
12690Sstevel@tonic-gate 			conn_head = (struct conn_ind *)0;
12700Sstevel@tonic-gate 		else {
12710Sstevel@tonic-gate 			conn_head->conn_prev = conn->conn_prev;
12720Sstevel@tonic-gate 			conn->conn_prev->conn_next = conn_head;
12730Sstevel@tonic-gate 		}
12740Sstevel@tonic-gate 		call = conn->conn_call;
12750Sstevel@tonic-gate 		free((char *)conn);
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 		/*
12780Sstevel@tonic-gate 		 * If we have already accepted the maximum number of
12790Sstevel@tonic-gate 		 * connections allowed on the command line, then drop
12800Sstevel@tonic-gate 		 * the oldest connection (for any protocol) before
12810Sstevel@tonic-gate 		 * accepting the new connection.  Unless explicitly
12820Sstevel@tonic-gate 		 * set on the command line, max_conns_allowed is -1.
12830Sstevel@tonic-gate 		 */
12840Sstevel@tonic-gate 		if (max_conns_allowed != -1 && num_conns >= max_conns_allowed)
12850Sstevel@tonic-gate 			conn_close_oldest();
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate 		/*
12880Sstevel@tonic-gate 		 * Create a new transport endpoint for the same proto as
12890Sstevel@tonic-gate 		 * the listener.
12900Sstevel@tonic-gate 		 */
12910Sstevel@tonic-gate 		new_fd = nfslib_transport_open(nconf);
12920Sstevel@tonic-gate 		if (new_fd == -1) {
12930Sstevel@tonic-gate 			call->udata.len = 0;
12940Sstevel@tonic-gate 			(void) t_snddis(fd, call);
12950Sstevel@tonic-gate 			(void) t_free((char *)call, T_CALL);
12960Sstevel@tonic-gate 			syslog(LOG_ERR, "Cannot establish transport over %s",
1297*7208Svv149972 			    nconf->nc_device);
12980Sstevel@tonic-gate 			continue;
12990Sstevel@tonic-gate 		}
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate 		/* Bind to a generic address/port for the accepting stream. */
13020Sstevel@tonic-gate 		if (t_bind(new_fd, (struct t_bind *)NULL,
13030Sstevel@tonic-gate 		    (struct t_bind *)NULL) == -1) {
13040Sstevel@tonic-gate 			nfslib_log_tli_error("t_bind", new_fd, nconf);
13050Sstevel@tonic-gate 			call->udata.len = 0;
13060Sstevel@tonic-gate 			(void) t_snddis(fd, call);
13070Sstevel@tonic-gate 			(void) t_free((char *)call, T_CALL);
13080Sstevel@tonic-gate 			(void) t_close(new_fd);
13090Sstevel@tonic-gate 			continue;
13100Sstevel@tonic-gate 		}
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 		while (t_accept(fd, new_fd, call) == -1) {
13130Sstevel@tonic-gate 			if (t_errno != TLOOK) {
13140Sstevel@tonic-gate #ifdef DEBUG
13150Sstevel@tonic-gate 				nfslib_log_tli_error("t_accept", fd, nconf);
13160Sstevel@tonic-gate #endif
13170Sstevel@tonic-gate 				call->udata.len = 0;
13180Sstevel@tonic-gate 				(void) t_snddis(fd, call);
13190Sstevel@tonic-gate 				(void) t_free((char *)call, T_CALL);
13200Sstevel@tonic-gate 				(void) t_close(new_fd);
13210Sstevel@tonic-gate 				goto do_next_conn;
13220Sstevel@tonic-gate 			}
13230Sstevel@tonic-gate 			while (event = t_look(fd)) {
13240Sstevel@tonic-gate 				switch (event) {
13250Sstevel@tonic-gate 				case T_LISTEN:
13260Sstevel@tonic-gate #ifdef DEBUG
13270Sstevel@tonic-gate 					printf(
13280Sstevel@tonic-gate "cots_listen_event(%s): T_LISTEN during accept processing\n", nconf->nc_proto);
13290Sstevel@tonic-gate #endif
13300Sstevel@tonic-gate 					(void) conn_get(fd, nconf, &conn_head);
13310Sstevel@tonic-gate 					continue;
13320Sstevel@tonic-gate 				case T_DISCONNECT:
13330Sstevel@tonic-gate #ifdef DEBUG
13340Sstevel@tonic-gate 					printf(
13350Sstevel@tonic-gate 	"cots_listen_event(%s): T_DISCONNECT during accept processing\n",
1336*7208Svv149972 					    nconf->nc_proto);
13370Sstevel@tonic-gate #endif
13380Sstevel@tonic-gate 					(void) discon_get(fd, nconf,
1339*7208Svv149972 					    &conn_head);
13400Sstevel@tonic-gate 					continue;
13410Sstevel@tonic-gate 				default:
13420Sstevel@tonic-gate 					syslog(LOG_ERR,
13430Sstevel@tonic-gate 			"unexpected event 0x%x during accept processing (%s)",
1344*7208Svv149972 					    event, nconf->nc_proto);
13450Sstevel@tonic-gate 					call->udata.len = 0;
13460Sstevel@tonic-gate 					(void) t_snddis(fd, call);
13470Sstevel@tonic-gate 					(void) t_free((char *)call, T_CALL);
13480Sstevel@tonic-gate 					(void) t_close(new_fd);
13490Sstevel@tonic-gate 					goto do_next_conn;
13500Sstevel@tonic-gate 				}
13510Sstevel@tonic-gate 			}
13520Sstevel@tonic-gate 		}
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate 		if (set_addrmask(new_fd, nconf, &addrmask) < 0) {
13550Sstevel@tonic-gate 			(void) syslog(LOG_ERR,
13560Sstevel@tonic-gate 			    "Cannot set address mask for %s",
1357*7208Svv149972 			    nconf->nc_netid);
13580Sstevel@tonic-gate 			return;
13590Sstevel@tonic-gate 		}
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate 		/* Tell KRPC about the new stream. */
13620Sstevel@tonic-gate 		if (Mysvc4 != NULL)
13630Sstevel@tonic-gate 			ret = (*Mysvc4)(new_fd, &addrmask, nconf,
1364*7208Svv149972 			    NFS4_KRPC_START, &call->addr);
13650Sstevel@tonic-gate 		else
13660Sstevel@tonic-gate 			ret = (*Mysvc)(new_fd, addrmask, nconf);
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 		if (ret < 0) {
13690Sstevel@tonic-gate 			if (errno != ENOTCONN) {
13700Sstevel@tonic-gate 				syslog(LOG_ERR,
13710Sstevel@tonic-gate 				    "unable to register new connection: %m");
13720Sstevel@tonic-gate 			} else {
13730Sstevel@tonic-gate 				/*
13740Sstevel@tonic-gate 				 * This is the only error that could be
13750Sstevel@tonic-gate 				 * caused by the client, so who was it?
13760Sstevel@tonic-gate 				 */
13770Sstevel@tonic-gate 				if (netdir_getbyaddr(nconf, &clnt_serv,
13780Sstevel@tonic-gate 				    &(call->addr)) == ND_OK &&
13790Sstevel@tonic-gate 				    clnt_serv->h_cnt > 0)
13800Sstevel@tonic-gate 					clnt = clnt_serv->h_hostservs->h_host;
13810Sstevel@tonic-gate 				else
13820Sstevel@tonic-gate 					clnt = clnt_uaddr = taddr2uaddr(nconf,
13830Sstevel@tonic-gate 					    &(call->addr));
13840Sstevel@tonic-gate 				/*
13850Sstevel@tonic-gate 				 * If we don't know who the client was,
13860Sstevel@tonic-gate 				 * remain silent.
13870Sstevel@tonic-gate 				 */
13880Sstevel@tonic-gate 				if (clnt)
13890Sstevel@tonic-gate 					syslog(LOG_ERR,
13900Sstevel@tonic-gate "unable to register new connection: client %s has dropped connection", clnt);
13910Sstevel@tonic-gate 				if (clnt_serv)
13920Sstevel@tonic-gate 					netdir_free(clnt_serv, ND_HOSTSERVLIST);
13930Sstevel@tonic-gate 				if (clnt_uaddr)
13940Sstevel@tonic-gate 					free(clnt_uaddr);
13950Sstevel@tonic-gate 			}
13960Sstevel@tonic-gate 			free(addrmask.buf);
13970Sstevel@tonic-gate 			(void) t_snddis(new_fd, (struct t_call *)0);
13980Sstevel@tonic-gate 			(void) t_free((char *)call, T_CALL);
13990Sstevel@tonic-gate 			(void) t_close(new_fd);
14000Sstevel@tonic-gate 			goto do_next_conn;
14010Sstevel@tonic-gate 		}
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 		free(addrmask.buf);
14040Sstevel@tonic-gate 		(void) t_free((char *)call, T_CALL);
14050Sstevel@tonic-gate 
14060Sstevel@tonic-gate 		/*
14070Sstevel@tonic-gate 		 * Poll on the new descriptor so that we get disconnect
14080Sstevel@tonic-gate 		 * and orderly release indications.
14090Sstevel@tonic-gate 		 */
14100Sstevel@tonic-gate 		num_conns++;
14110Sstevel@tonic-gate 		add_to_poll_list(new_fd, nconf);
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 		/* Reset nconf in case it has been moved. */
14140Sstevel@tonic-gate 		nconf = &conn_polled[conn_index].nc;
14150Sstevel@tonic-gate do_next_conn:;
14160Sstevel@tonic-gate 	}
14170Sstevel@tonic-gate }
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate static int
14200Sstevel@tonic-gate do_poll_cots_action(int fd, int conn_index)
14210Sstevel@tonic-gate {
14220Sstevel@tonic-gate 	char buf[256];
14230Sstevel@tonic-gate 	int event;
14240Sstevel@tonic-gate 	int i1;
14250Sstevel@tonic-gate 	int flags;
14260Sstevel@tonic-gate 	struct conn_entry *connent = &conn_polled[conn_index];
14270Sstevel@tonic-gate 	struct netconfig *nconf = &(connent->nc);
14280Sstevel@tonic-gate 	const char *errorstr;
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	while (event = t_look(fd)) {
14310Sstevel@tonic-gate 		switch (event) {
14320Sstevel@tonic-gate 		case T_LISTEN:
14330Sstevel@tonic-gate #ifdef DEBUG
14340Sstevel@tonic-gate printf("do_poll_cots_action(%s,%d): T_LISTEN event\n", nconf->nc_proto, fd);
14350Sstevel@tonic-gate #endif
14360Sstevel@tonic-gate 			cots_listen_event(fd, conn_index);
14370Sstevel@tonic-gate 			break;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 		case T_DATA:
14400Sstevel@tonic-gate #ifdef DEBUG
14410Sstevel@tonic-gate printf("do_poll_cots_action(%d,%s): T_DATA event\n", fd, nconf->nc_proto);
14420Sstevel@tonic-gate #endif
14430Sstevel@tonic-gate 			/*
14440Sstevel@tonic-gate 			 * Receive a private notification from CONS rpcmod.
14450Sstevel@tonic-gate 			 */
14460Sstevel@tonic-gate 			i1 = t_rcv(fd, buf, sizeof (buf), &flags);
14470Sstevel@tonic-gate 			if (i1 == -1) {
14480Sstevel@tonic-gate 				syslog(LOG_ERR, "t_rcv failed");
14490Sstevel@tonic-gate 				break;
14500Sstevel@tonic-gate 			}
14510Sstevel@tonic-gate 			if (i1 < sizeof (int))
14520Sstevel@tonic-gate 				break;
14530Sstevel@tonic-gate 			i1 = BE32_TO_U32(buf);
14540Sstevel@tonic-gate 			if (i1 == 1 || i1 == 2) {
14550Sstevel@tonic-gate 				/*
14560Sstevel@tonic-gate 				 * This connection has been idle for too long,
14570Sstevel@tonic-gate 				 * so release it as politely as we can.  If we
14580Sstevel@tonic-gate 				 * have already initiated an orderly release
14590Sstevel@tonic-gate 				 * and we get notified that the stream is
14600Sstevel@tonic-gate 				 * still idle, pull the plug.  This prevents
14610Sstevel@tonic-gate 				 * hung connections from continuing to consume
14620Sstevel@tonic-gate 				 * resources.
14630Sstevel@tonic-gate 				 */
14640Sstevel@tonic-gate #ifdef DEBUG
14650Sstevel@tonic-gate printf("do_poll_cots_action(%s,%d): ", nconf->nc_proto, fd);
14660Sstevel@tonic-gate printf("initiating orderly release of idle connection\n");
14670Sstevel@tonic-gate #endif
14680Sstevel@tonic-gate 				if (nconf->nc_semantics == NC_TPI_COTS ||
1469*7208Svv149972 				    connent->closing != 0) {
14700Sstevel@tonic-gate 					(void) t_snddis(fd, (struct t_call *)0);
14710Sstevel@tonic-gate 					goto fdclose;
14720Sstevel@tonic-gate 				}
14730Sstevel@tonic-gate 				/*
14740Sstevel@tonic-gate 				 * For NC_TPI_COTS_ORD, the stream is closed
14750Sstevel@tonic-gate 				 * and removed from the poll list when the
14760Sstevel@tonic-gate 				 * T_ORDREL is received from the provider.  We
14770Sstevel@tonic-gate 				 * don't wait for it here because it may take
14780Sstevel@tonic-gate 				 * a while for the transport to shut down.
14790Sstevel@tonic-gate 				 */
14800Sstevel@tonic-gate 				if (t_sndrel(fd) == -1) {
14810Sstevel@tonic-gate 					syslog(LOG_ERR,
14820Sstevel@tonic-gate 					"unable to send orderly release %m");
14830Sstevel@tonic-gate 				}
14840Sstevel@tonic-gate 				connent->closing = 1;
14850Sstevel@tonic-gate 			} else
14860Sstevel@tonic-gate 				syslog(LOG_ERR,
14870Sstevel@tonic-gate 				"unexpected event from CONS rpcmod %d", i1);
14880Sstevel@tonic-gate 			break;
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 		case T_ORDREL:
14910Sstevel@tonic-gate #ifdef DEBUG
14920Sstevel@tonic-gate printf("do_poll_cots_action(%s,%d): T_ORDREL event\n", nconf->nc_proto, fd);
14930Sstevel@tonic-gate #endif
14940Sstevel@tonic-gate 			/* Perform an orderly release. */
14950Sstevel@tonic-gate 			if (t_rcvrel(fd) == 0) {
14960Sstevel@tonic-gate 				/* T_ORDREL on listen fd's should be ignored */
14970Sstevel@tonic-gate 				if (!is_listen_fd_index(conn_index)) {
14980Sstevel@tonic-gate 					(void) t_sndrel(fd);
14990Sstevel@tonic-gate 					goto fdclose;
15000Sstevel@tonic-gate 				}
15010Sstevel@tonic-gate 				break;
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 			} else if (t_errno == TLOOK) {
15040Sstevel@tonic-gate 				break;
15050Sstevel@tonic-gate 			} else {
15060Sstevel@tonic-gate 				nfslib_log_tli_error("t_rcvrel", fd, nconf);
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 				/*
15090Sstevel@tonic-gate 				 * check to make sure we do not close
15100Sstevel@tonic-gate 				 * listen fd
15110Sstevel@tonic-gate 				 */
15120Sstevel@tonic-gate 				if (is_listen_fd_index(conn_index))
15130Sstevel@tonic-gate 					break;
15140Sstevel@tonic-gate 				else
15150Sstevel@tonic-gate 					goto fdclose;
15160Sstevel@tonic-gate 			}
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 		case T_DISCONNECT:
15190Sstevel@tonic-gate #ifdef DEBUG
15200Sstevel@tonic-gate printf("do_poll_cots_action(%s,%d): T_DISCONNECT event\n", nconf->nc_proto, fd);
15210Sstevel@tonic-gate #endif
15220Sstevel@tonic-gate 			if (t_rcvdis(fd, (struct t_discon *)NULL) == -1)
15230Sstevel@tonic-gate 				nfslib_log_tli_error("t_rcvdis", fd, nconf);
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate 			/*
15260Sstevel@tonic-gate 			 * T_DISCONNECT on listen fd's should be ignored.
15270Sstevel@tonic-gate 			 */
15280Sstevel@tonic-gate 			if (is_listen_fd_index(conn_index))
15290Sstevel@tonic-gate 				break;
15300Sstevel@tonic-gate 			else
15310Sstevel@tonic-gate 				goto fdclose;
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate 		case T_ERROR:
15340Sstevel@tonic-gate 		default:
15350Sstevel@tonic-gate 			if (event == T_ERROR || t_errno == TSYSERR) {
1536*7208Svv149972 				if ((errorstr = strerror(errno)) == NULL) {
1537*7208Svv149972 					(void) sprintf(buf,
1538*7208Svv149972 					    "Unknown error num %d", errno);
1539*7208Svv149972 					errorstr = (const char *) buf;
1540*7208Svv149972 				}
15410Sstevel@tonic-gate 			} else if (event == -1)
15420Sstevel@tonic-gate 				errorstr = t_strerror(t_errno);
15430Sstevel@tonic-gate 			else
15440Sstevel@tonic-gate 				errorstr = "";
15450Sstevel@tonic-gate 			syslog(LOG_ERR,
15460Sstevel@tonic-gate 			    "unexpected TLI event (0x%x) on "
15470Sstevel@tonic-gate 			    "connection-oriented transport(%s,%d):%s",
15480Sstevel@tonic-gate 			    event, nconf->nc_proto, fd, errorstr);
15490Sstevel@tonic-gate fdclose:
15500Sstevel@tonic-gate 			num_conns--;
15510Sstevel@tonic-gate 			remove_from_poll_list(fd);
15520Sstevel@tonic-gate 			(void) t_close(fd);
15530Sstevel@tonic-gate 			return (0);
15540Sstevel@tonic-gate 		}
15550Sstevel@tonic-gate 	}
15560Sstevel@tonic-gate 
15570Sstevel@tonic-gate 	return (0);
15580Sstevel@tonic-gate }
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate static char *
15610Sstevel@tonic-gate serv_name_to_port_name(char *name)
15620Sstevel@tonic-gate {
15630Sstevel@tonic-gate 	/*
15640Sstevel@tonic-gate 	 * Map service names (used primarily in logging) to
15650Sstevel@tonic-gate 	 * RPC port names (used by netdir_*() routines).
15660Sstevel@tonic-gate 	 */
15670Sstevel@tonic-gate 	if (strcmp(name, "NFS") == 0) {
15680Sstevel@tonic-gate 		return ("nfs");
15690Sstevel@tonic-gate 	} else if (strcmp(name, "NLM") == 0) {
15700Sstevel@tonic-gate 		return ("lockd");
15710Sstevel@tonic-gate 	} else if (strcmp(name, "NFS4_CALLBACK") == 0) {
15720Sstevel@tonic-gate 		return ("nfs4_callback");
15730Sstevel@tonic-gate 	}
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 	return ("unrecognized");
15760Sstevel@tonic-gate }
15770Sstevel@tonic-gate 
15780Sstevel@tonic-gate static int
15790Sstevel@tonic-gate bind_to_provider(char *provider, char *serv, struct netbuf **addr,
15800Sstevel@tonic-gate 		struct netconfig **retnconf)
15810Sstevel@tonic-gate {
15820Sstevel@tonic-gate 	struct netconfig *nconf;
15830Sstevel@tonic-gate 	NCONF_HANDLE *nc;
15840Sstevel@tonic-gate 	struct nd_hostserv hs;
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	hs.h_host = HOST_SELF;
15870Sstevel@tonic-gate 	hs.h_serv = serv_name_to_port_name(serv);
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
15900Sstevel@tonic-gate 		syslog(LOG_ERR, "setnetconfig failed: %m");
15910Sstevel@tonic-gate 		return (-1);
15920Sstevel@tonic-gate 	}
15930Sstevel@tonic-gate 	while (nconf = getnetconfig(nc)) {
15940Sstevel@tonic-gate 		if (OK_TPI_TYPE(nconf) &&
15950Sstevel@tonic-gate 		    strcmp(nconf->nc_device, provider) == 0) {
15960Sstevel@tonic-gate 			*retnconf = nconf;
15970Sstevel@tonic-gate 			return (nfslib_bindit(nconf, addr, &hs,
1598*7208Svv149972 			    listen_backlog));
15990Sstevel@tonic-gate 		}
16000Sstevel@tonic-gate 	}
16010Sstevel@tonic-gate 	(void) endnetconfig(nc);
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 	syslog(LOG_ERR, "couldn't find netconfig entry for provider %s",
16040Sstevel@tonic-gate 	    provider);
16050Sstevel@tonic-gate 	return (-1);
16060Sstevel@tonic-gate }
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate static int
16090Sstevel@tonic-gate bind_to_proto(NETSELDECL(proto), char *serv, struct netbuf **addr,
16100Sstevel@tonic-gate 		struct netconfig **retnconf)
16110Sstevel@tonic-gate {
16120Sstevel@tonic-gate 	struct netconfig *nconf;
16130Sstevel@tonic-gate 	NCONF_HANDLE *nc = NULL;
16140Sstevel@tonic-gate 	struct nd_hostserv hs;
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 	hs.h_host = HOST_SELF;
16170Sstevel@tonic-gate 	hs.h_serv = serv_name_to_port_name(serv);
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
16200Sstevel@tonic-gate 		syslog(LOG_ERR, "setnetconfig failed: %m");
16210Sstevel@tonic-gate 		return (-1);
16220Sstevel@tonic-gate 	}
16230Sstevel@tonic-gate 	while (nconf = getnetconfig(nc)) {
16240Sstevel@tonic-gate 		if (OK_TPI_TYPE(nconf) && NETSELEQ(nconf->nc_proto, proto)) {
16250Sstevel@tonic-gate 			*retnconf = nconf;
16260Sstevel@tonic-gate 			return (nfslib_bindit(nconf, addr, &hs,
1627*7208Svv149972 			    listen_backlog));
16280Sstevel@tonic-gate 		}
16290Sstevel@tonic-gate 	}
16300Sstevel@tonic-gate 	(void) endnetconfig(nc);
16310Sstevel@tonic-gate 
16320Sstevel@tonic-gate 	syslog(LOG_ERR, "couldn't find netconfig entry for protocol %s",
16330Sstevel@tonic-gate 	    proto);
16340Sstevel@tonic-gate 	return (-1);
16350Sstevel@tonic-gate }
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate #include <netinet/in.h>
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate /*
16400Sstevel@tonic-gate  * Create an address mask appropriate for the transport.
16410Sstevel@tonic-gate  * The mask is used to obtain the host-specific part of
16420Sstevel@tonic-gate  * a network address when comparing addresses.
16430Sstevel@tonic-gate  * For an internet address the host-specific part is just
16440Sstevel@tonic-gate  * the 32 bit IP address and this part of the mask is set
16450Sstevel@tonic-gate  * to all-ones. The port number part of the mask is zeroes.
16460Sstevel@tonic-gate  */
16470Sstevel@tonic-gate static int
16480Sstevel@tonic-gate set_addrmask(fd, nconf, mask)
16490Sstevel@tonic-gate 	struct netconfig *nconf;
16500Sstevel@tonic-gate 	struct netbuf *mask;
16510Sstevel@tonic-gate {
16520Sstevel@tonic-gate 	struct t_info info;
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate 	/*
16550Sstevel@tonic-gate 	 * Find the size of the address we need to mask.
16560Sstevel@tonic-gate 	 */
16570Sstevel@tonic-gate 	if (t_getinfo(fd, &info) < 0) {
16580Sstevel@tonic-gate 		t_error("t_getinfo");
16590Sstevel@tonic-gate 		return (-1);
16600Sstevel@tonic-gate 	}
16610Sstevel@tonic-gate 	mask->len = mask->maxlen = info.addr;
16620Sstevel@tonic-gate 	if (info.addr <= 0) {
16630Sstevel@tonic-gate 		syslog(LOG_ERR, "set_addrmask: address size: %ld",
16640Sstevel@tonic-gate 			info.addr);
16650Sstevel@tonic-gate 		return (-1);
16660Sstevel@tonic-gate 	}
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 	mask->buf = (char *)malloc(mask->len);
16690Sstevel@tonic-gate 	if (mask->buf == NULL) {
16700Sstevel@tonic-gate 		syslog(LOG_ERR, "set_addrmask: no memory");
16710Sstevel@tonic-gate 		return (-1);
16720Sstevel@tonic-gate 	}
16730Sstevel@tonic-gate 	(void) memset(mask->buf, 0, mask->len);	/* reset all mask bits */
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate 	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
16760Sstevel@tonic-gate 		/*
16770Sstevel@tonic-gate 		 * Set the mask so that the port is ignored.
16780Sstevel@tonic-gate 		 */
16790Sstevel@tonic-gate 		/* LINTED pointer alignment */
16800Sstevel@tonic-gate 		((struct sockaddr_in *)mask->buf)->sin_addr.s_addr =
16810Sstevel@tonic-gate 								(ulong_t)~0;
16820Sstevel@tonic-gate 		/* LINTED pointer alignment */
16830Sstevel@tonic-gate 		((struct sockaddr_in *)mask->buf)->sin_family =
16840Sstevel@tonic-gate 								(ushort_t)~0;
16850Sstevel@tonic-gate 	} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
16860Sstevel@tonic-gate 		/* LINTED pointer alignment */
16870Sstevel@tonic-gate 		(void) memset(&((struct sockaddr_in6 *)mask->buf)->sin6_addr,
16880Sstevel@tonic-gate 			(uchar_t)~0, sizeof (struct in6_addr));
16890Sstevel@tonic-gate 		/* LINTED pointer alignment */
16900Sstevel@tonic-gate 		((struct sockaddr_in6 *)mask->buf)->sin6_family =
16910Sstevel@tonic-gate 								(ushort_t)~0;
16920Sstevel@tonic-gate 	} else {
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 		/*
16950Sstevel@tonic-gate 		 * Set all mask bits.
16960Sstevel@tonic-gate 		 */
16970Sstevel@tonic-gate 		(void) memset(mask->buf, 0xFF, mask->len);
16980Sstevel@tonic-gate 	}
16990Sstevel@tonic-gate 	return (0);
17000Sstevel@tonic-gate }
17010Sstevel@tonic-gate 
17020Sstevel@tonic-gate /*
17030Sstevel@tonic-gate  * For listen fd's index is always less than end_listen_fds.
17040Sstevel@tonic-gate  * end_listen_fds is defined externally in the daemon that uses this library.
17050Sstevel@tonic-gate  * It's value is equal to the number of open file descriptors after the
17060Sstevel@tonic-gate  * last listen end point was opened but before any connection was accepted.
17070Sstevel@tonic-gate  */
17080Sstevel@tonic-gate static int
17090Sstevel@tonic-gate is_listen_fd_index(int index)
17100Sstevel@tonic-gate {
17110Sstevel@tonic-gate 	return (index < end_listen_fds);
17120Sstevel@tonic-gate }
1713