17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
545916cdjpk * Common Development and Distribution License (the "License").
645916cdjpk * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
21bbaa8b6Dan Kruchinin
227c478bdstevel@tonic-gate/*
239ff75adSurya Prakki * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
24bbaa8b6Dan Kruchinin * Copyright (c) 2012 by Delphix. All rights reserved.
255539384Marcel Telka * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
2609b0d01Gary Mills * Copyright 2014 Gary Mills
274a03041Dan Kruchinin */
284a03041Dan Kruchinin
297c478bdstevel@tonic-gate
307c478bdstevel@tonic-gate/*
317c478bdstevel@tonic-gate * nfs_tbind.c, common part for nfsd and lockd.
327c478bdstevel@tonic-gate */
337c478bdstevel@tonic-gate
347c478bdstevel@tonic-gate#include <tiuser.h>
357c478bdstevel@tonic-gate#include <fcntl.h>
367c478bdstevel@tonic-gate#include <netconfig.h>
377c478bdstevel@tonic-gate#include <stropts.h>
387c478bdstevel@tonic-gate#include <errno.h>
397c478bdstevel@tonic-gate#include <syslog.h>
407c478bdstevel@tonic-gate#include <rpc/rpc.h>
417c478bdstevel@tonic-gate#include <sys/time.h>
427c478bdstevel@tonic-gate#include <sys/resource.h>
437c478bdstevel@tonic-gate#include <signal.h>
447c478bdstevel@tonic-gate#include <netdir.h>
457c478bdstevel@tonic-gate#include <unistd.h>
467c478bdstevel@tonic-gate#include <string.h>
477c478bdstevel@tonic-gate#include <netinet/tcp.h>
487c478bdstevel@tonic-gate#include <malloc.h>
497c478bdstevel@tonic-gate#include <stdlib.h>
507c478bdstevel@tonic-gate#include "nfs_tbind.h"
517c478bdstevel@tonic-gate#include <nfs/nfs.h>
527c478bdstevel@tonic-gate#include <nfs/nfs_acl.h>
537c478bdstevel@tonic-gate#include <nfs/nfssys.h>
547c478bdstevel@tonic-gate#include <nfs/nfs4.h>
5545916cdjpk#include <zone.h>
5645916cdjpk#include <sys/socket.h>
5745916cdjpk#include <tsol/label.h>
587c478bdstevel@tonic-gate
597c478bdstevel@tonic-gate/*
607c478bdstevel@tonic-gate * Determine valid semantics for most applications.
617c478bdstevel@tonic-gate */
627c478bdstevel@tonic-gate#define	OK_TPI_TYPE(_nconf) \
637c478bdstevel@tonic-gate	(_nconf->nc_semantics == NC_TPI_CLTS || \
647c478bdstevel@tonic-gate	_nconf->nc_semantics == NC_TPI_COTS || \
657c478bdstevel@tonic-gate	_nconf->nc_semantics == NC_TPI_COTS_ORD)
667c478bdstevel@tonic-gate
677c478bdstevel@tonic-gate#define	BE32_TO_U32(a) \
687c478bdstevel@tonic-gate	((((ulong_t)((uchar_t *)a)[0] & 0xFF) << (ulong_t)24) | \
697c478bdstevel@tonic-gate	(((ulong_t)((uchar_t *)a)[1] & 0xFF) << (ulong_t)16) | \
707c478bdstevel@tonic-gate	(((ulong_t)((uchar_t *)a)[2] & 0xFF) << (ulong_t)8)  | \
717c478bdstevel@tonic-gate	((ulong_t)((uchar_t *)a)[3] & 0xFF))
727c478bdstevel@tonic-gate
737c478bdstevel@tonic-gate/*
747c478bdstevel@tonic-gate * Number of elements to add to the poll array on each allocation.
757c478bdstevel@tonic-gate */
767c478bdstevel@tonic-gate#define	POLL_ARRAY_INC_SIZE	64
777c478bdstevel@tonic-gate
787c478bdstevel@tonic-gate/*
797c478bdstevel@tonic-gate * Number of file descriptors by which the process soft limit may be
807c478bdstevel@tonic-gate * increased on each call to nofile_increase(0).
817c478bdstevel@tonic-gate */
827c478bdstevel@tonic-gate#define	NOFILE_INC_SIZE	64
837c478bdstevel@tonic-gate
842081ac1Dai Ngo/*
852081ac1Dai Ngo * Default TCP send and receive buffer size of NFS server.
862081ac1Dai Ngo */
872081ac1Dai Ngo#define	NFSD_TCP_BUFSZ	(1024*1024)
882081ac1Dai Ngo
897c478bdstevel@tonic-gatestruct conn_ind {
907c478bdstevel@tonic-gate	struct conn_ind *conn_next;
917c478bdstevel@tonic-gate	struct conn_ind *conn_prev;
927c478bdstevel@tonic-gate	struct t_call   *conn_call;
937c478bdstevel@tonic-gate};
947c478bdstevel@tonic-gate
957c478bdstevel@tonic-gatestruct conn_entry {
967c478bdstevel@tonic-gate	bool_t			closing;
977c478bdstevel@tonic-gate	struct netconfig	nc;
987c478bdstevel@tonic-gate};
997c478bdstevel@tonic-gate
1007c478bdstevel@tonic-gate/*
1017c478bdstevel@tonic-gate * this file contains transport routines common to nfsd and lockd
1027c478bdstevel@tonic-gate */
1037c478bdstevel@tonic-gatestatic	int	nofile_increase(int);
1047c478bdstevel@tonic-gatestatic	int	reuseaddr(int);
10545916cdjpkstatic	int	recvucred(int);
10645916cdjpkstatic  int	anonmlp(int);
1077c478bdstevel@tonic-gatestatic	void	add_to_poll_list(int, struct netconfig *);
1087c478bdstevel@tonic-gatestatic	char	*serv_name_to_port_name(char *);
1097c478bdstevel@tonic-gatestatic	int	bind_to_proto(char *, char *, struct netbuf **,
1107c478bdstevel@tonic-gate				struct netconfig **);
1117c478bdstevel@tonic-gatestatic	int	bind_to_provider(char *, char *, struct netbuf **,
1127c478bdstevel@tonic-gate					struct netconfig **);
1137c478bdstevel@tonic-gatestatic	void	conn_close_oldest(void);
1147c478bdstevel@tonic-gatestatic	boolean_t conn_get(int, struct netconfig *, struct conn_ind **);
1157c478bdstevel@tonic-gatestatic	void	cots_listen_event(int, int);
1167c478bdstevel@tonic-gatestatic	int	discon_get(int, struct netconfig *, struct conn_ind **);
1177c478bdstevel@tonic-gatestatic	int	do_poll_clts_action(int, int);
1187c478bdstevel@tonic-gatestatic	int	do_poll_cots_action(int, int);
1197c478bdstevel@tonic-gatestatic	void	remove_from_poll_list(int);
1207c478bdstevel@tonic-gatestatic	int	set_addrmask(int, struct netconfig *, struct netbuf *);
1217c478bdstevel@tonic-gatestatic	int	is_listen_fd_index(int);
1227c478bdstevel@tonic-gate
1237c478bdstevel@tonic-gatestatic	struct pollfd *poll_array;
1247c478bdstevel@tonic-gatestatic	struct conn_entry *conn_polled;
1257c478bdstevel@tonic-gatestatic	int	num_conns;		/* Current number of connections */
1267c478bdstevel@tonic-gateint		(*Mysvc4)(int, struct netbuf *, struct netconfig *, int,
1277c478bdstevel@tonic-gate		struct netbuf *);
1282081ac1Dai Ngostatic int	setopt(int fd, int level, int name, int value);
1292081ac1Dai Ngostatic int	get_opt(int fd, int level, int name);
1302081ac1Dai Ngostatic void	nfslib_set_sockbuf(int fd);
1317c478bdstevel@tonic-gate
1327c478bdstevel@tonic-gate/*
1337c478bdstevel@tonic-gate * Called to create and prepare a transport descriptor for in-kernel
1347c478bdstevel@tonic-gate * RPC service.
1357c478bdstevel@tonic-gate * Returns -1 on failure and a valid descriptor on success.
1367c478bdstevel@tonic-gate */
1377c478bdstevel@tonic-gateint
1387c478bdstevel@tonic-gatenfslib_transport_open(struct netconfig *nconf)
1397c478bdstevel@tonic-gate{
1407c478bdstevel@tonic-gate	int fd;
1417c478bdstevel@tonic-gate	struct strioctl	strioc;
1427c478bdstevel@tonic-gate
1437c478bdstevel@tonic-gate	if ((nconf == (struct netconfig *)NULL) ||
1447c478bdstevel@tonic-gate	    (nconf->nc_device == (char *)NULL)) {
1457c478bdstevel@tonic-gate		syslog(LOG_ERR, "no netconfig device");
1467c478bdstevel@tonic-gate		return (-1);
1477c478bdstevel@tonic-gate	}
1487c478bdstevel@tonic-gate
1497c478bdstevel@tonic-gate	/*
1507c478bdstevel@tonic-gate	 * Open the transport device.
1517c478bdstevel@tonic-gate	 */
1527c478bdstevel@tonic-gate	fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL);
1537c478bdstevel@tonic-gate	if (fd == -1) {
1547c478bdstevel@tonic-gate		if (t_errno == TSYSERR && errno == EMFILE &&
1557c478bdstevel@tonic-gate		    (nofile_increase(0) == 0)) {
1567c478bdstevel@tonic-gate			/* Try again with a higher NOFILE limit. */
1577c478bdstevel@tonic-gate			fd = t_open(nconf->nc_device, O_RDWR,
158e810a98vv			    (struct t_info *)NULL);
1597c478bdstevel@tonic-gate		}
1607c478bdstevel@tonic-gate		if (fd == -1) {
1617c478bdstevel@tonic-gate			syslog(LOG_ERR, "t_open %s failed:  t_errno %d, %m",
1627c478bdstevel@tonic-gate			    nconf->nc_device, t_errno);
1637c478bdstevel@tonic-gate			return (-1);
1647c478bdstevel@tonic-gate		}
1657c478bdstevel@tonic-gate	}
1667c478bdstevel@tonic-gate
1677c478bdstevel@tonic-gate	/*
1687c478bdstevel@tonic-gate	 * Pop timod because the RPC module must be as close as possible
1697c478bdstevel@tonic-gate	 * to the transport.
1707c478bdstevel@tonic-gate	 */
1717c478bdstevel@tonic-gate	if (ioctl(fd, I_POP, 0) < 0) {
1727c478bdstevel@tonic-gate		syslog(LOG_ERR, "I_POP of timod failed: %m");
1737c478bdstevel@tonic-gate		(void) t_close(fd);
1747c478bdstevel@tonic-gate		return (-1);
1757c478bdstevel@tonic-gate	}
1767c478bdstevel@tonic-gate
1777c478bdstevel@tonic-gate	/*
1787c478bdstevel@tonic-gate	 * Common code for CLTS and COTS transports
1797c478bdstevel@tonic-gate	 */
1807c478bdstevel@tonic-gate	if (ioctl(fd, I_PUSH, "rpcmod") < 0) {
1817c478bdstevel@tonic-gate		syslog(LOG_ERR, "I_PUSH of rpcmod failed: %m");
1827c478bdstevel@tonic-gate		(void) t_close(fd);
1837c478bdstevel@tonic-gate		return (-1);
1847c478bdstevel@tonic-gate	}
1857c478bdstevel@tonic-gate
1867c478bdstevel@tonic-gate	strioc.ic_cmd = RPC_SERVER;
1877c478bdstevel@tonic-gate	strioc.ic_dp = (char *)0;
1887c478bdstevel@tonic-gate	strioc.ic_len = 0;
1897c478bdstevel@tonic-gate	strioc.ic_timout = -1;
1907c478bdstevel@tonic-gate
1917c478bdstevel@tonic-gate	/* Tell rpcmod to act like a server stream. */
1927c478bdstevel@tonic-gate	if (ioctl(fd, I_STR, &strioc) < 0) {
1937c478bdstevel@tonic-gate		syslog(LOG_ERR, "rpcmod set-up ioctl failed: %m");
1947c478bdstevel@tonic-gate		(void) t_close(fd);
1957c478bdstevel@tonic-gate		return (-1);
1967c478bdstevel@tonic-gate	}
1977c478bdstevel@tonic-gate
1987c478bdstevel@tonic-gate	/*
1997c478bdstevel@tonic-gate	 * Re-push timod so that we will still be doing TLI
2007c478bdstevel@tonic-gate	 * operations on the descriptor.
2017c478bdstevel@tonic-gate	 */
2027c478bdstevel@tonic-gate	if (ioctl(fd, I_PUSH, "timod") < 0) {
2037c478bdstevel@tonic-gate		syslog(LOG_ERR, "I_PUSH of timod failed: %m");
2047c478bdstevel@tonic-gate		(void) t_close(fd);
2057c478bdstevel@tonic-gate		return (-1);
2067c478bdstevel@tonic-gate	}
2077c478bdstevel@tonic-gate
208e810a98vv	/*
209e810a98vv	 * Enable options of returning the ip's for udp.
210e810a98vv	 */
211e810a98vv	if (strcmp(nconf->nc_netid, "udp6") == 0)
212e810a98vv		__rpc_tli_set_options(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1);
213e810a98vv	else if (strcmp(nconf->nc_netid, "udp") == 0)
214e810a98vv		__rpc_tli_set_options(fd, IPPROTO_IP, IP_RECVDSTADDR, 1);
215e810a98vv
2167c478bdstevel@tonic-gate	return (fd);
2177c478bdstevel@tonic-gate}
2187c478bdstevel@tonic-gate
2197c478bdstevel@tonic-gatestatic int
2207c478bdstevel@tonic-gatenofile_increase(int limit)
2217c478bdstevel@tonic-gate{
2227c478bdstevel@tonic-gate	struct rlimit rl;
2237c478bdstevel@tonic-gate
2247c478bdstevel@tonic-gate	if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
2257c478bdstevel@tonic-gate		syslog(LOG_ERR, "getrlimit of NOFILE failed: %m");
2267c478bdstevel@tonic-gate		return (-1);
2277c478bdstevel@tonic-gate	}
2287c478bdstevel@tonic-gate
2297c478bdstevel@tonic-gate	if (limit > 0)
2307c478bdstevel@tonic-gate		rl.rlim_cur = limit;
2317c478bdstevel@tonic-gate	else
2327c478bdstevel@tonic-gate		rl.rlim_cur += NOFILE_INC_SIZE;
2337c478bdstevel@tonic-gate
2347c478bdstevel@tonic-gate	if (rl.rlim_cur > rl.rlim_max &&
2357c478bdstevel@tonic-gate	    rl.rlim_max != RLIM_INFINITY)
2367c478bdstevel@tonic-gate		rl.rlim_max = rl.rlim_cur;
2377c478bdstevel@tonic-gate
2387c478bdstevel@tonic-gate	if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
2397c478bdstevel@tonic-gate		syslog(LOG_ERR, "setrlimit of NOFILE to %d failed: %m",
240e810a98vv		    rl.rlim_cur);
2417c478bdstevel@tonic-gate		return (-1);
2427c478bdstevel@tonic-gate	}
2437c478bdstevel@tonic-gate
2447c478bdstevel@tonic-gate	return (0);
2457c478bdstevel@tonic-gate}
2467c478bdstevel@tonic-gate
2472081ac1Dai Ngostatic void
2482081ac1Dai Ngonfslib_set_sockbuf(int fd)
2492081ac1Dai Ngo{
2502081ac1Dai Ngo	int curval, val;
2512081ac1Dai Ngo
2522081ac1Dai Ngo	val = NFSD_TCP_BUFSZ;
2532081ac1Dai Ngo
2542081ac1Dai Ngo	curval = get_opt(fd, SOL_SOCKET, SO_SNDBUF);
2552081ac1Dai Ngo	syslog(LOG_DEBUG, "Current SO_SNDBUF value is %d", curval);
2562081ac1Dai Ngo	if ((curval != -1) && (curval < val)) {
2572081ac1Dai Ngo		syslog(LOG_DEBUG, "Set SO_SNDBUF  option to %d", val);
2582081ac1Dai Ngo		if (setopt(fd, SOL_SOCKET, SO_SNDBUF, val) < 0) {
2592081ac1Dai Ngo			syslog(LOG_ERR,
2602081ac1Dai Ngo			    "couldn't set SO_SNDBUF to %d - t_errno = %d",
2612081ac1Dai Ngo			    val, t_errno);
2622081ac1Dai Ngo			syslog(LOG_ERR,
2632081ac1Dai Ngo			    "Check and increase system-wide tcp_max_buf");
2642081ac1Dai Ngo		}
2652081ac1Dai Ngo	}
2662081ac1Dai Ngo
2672081ac1Dai Ngo	curval = get_opt(fd, SOL_SOCKET, SO_RCVBUF);
2682081ac1Dai Ngo	syslog(LOG_DEBUG, "Current SO_RCVBUF value is %d", curval);
2692081ac1Dai Ngo	if ((curval != -1) && (curval < val)) {
2702081ac1Dai Ngo		syslog(LOG_DEBUG, "Set SO_RCVBUF  option to %d", val);
2712081ac1Dai Ngo		if (setopt(fd, SOL_SOCKET, SO_RCVBUF, val) < 0) {
2722081ac1Dai Ngo			syslog(LOG_ERR,
2732081ac1Dai Ngo			    "couldn't set SO_RCVBUF to %d - t_errno = %d",
2742081ac1Dai Ngo			    val, t_errno);
2752081ac1Dai Ngo			syslog(LOG_ERR,
2762081ac1Dai Ngo			    "Check and increase system-wide tcp_max_buf");
2772081ac1Dai Ngo		}
2782081ac1Dai Ngo	}
2792081ac1Dai Ngo}
2802081ac1Dai Ngo
2817c478bdstevel@tonic-gateint
2827c478bdstevel@tonic-gatenfslib_bindit(struct netconfig *nconf, struct netbuf **addr,
2837c478bdstevel@tonic-gate	struct nd_hostserv *hs, int backlog)
2847c478bdstevel@tonic-gate{
2857c478bdstevel@tonic-gate	int fd;
2867c478bdstevel@tonic-gate	struct t_bind  *ntb;
2877c478bdstevel@tonic-gate	struct t_bind tb;
2887c478bdstevel@tonic-gate	struct nd_addrlist *addrlist;
2897c478bdstevel@tonic-gate	struct t_optmgmt req, resp;
2907c478bdstevel@tonic-gate	struct opthdr *opt;
2917c478bdstevel@tonic-gate	char reqbuf[128];
2927c478bdstevel@tonic-gate	bool_t use_any = FALSE;
29345916cdjpk	bool_t gzone = TRUE;
2947c478bdstevel@tonic-gate
2957c478bdstevel@tonic-gate	if ((fd = nfslib_transport_open(nconf)) == -1) {
2967c478bdstevel@tonic-gate		syslog(LOG_ERR, "cannot establish transport service over %s",
297e810a98vv		    nconf->nc_device);
2987c478bdstevel@tonic-gate		return (-1);
2997c478bdstevel@tonic-gate	}
3007c478bdstevel@tonic-gate
3017c478bdstevel@tonic-gate	addrlist = (struct nd_addrlist *)NULL;
3027c478bdstevel@tonic-gate
3037c478bdstevel@tonic-gate	/* nfs4_callback service does not used a fieed port number */
3047c478bdstevel@tonic-gate
3057c478bdstevel@tonic-gate	if (strcmp(hs->h_serv, "nfs4_callback") == 0) {
3067c478bdstevel@tonic-gate		tb.addr.maxlen = 0;
3077c478bdstevel@tonic-gate		tb.addr.len = 0;
3087c478bdstevel@tonic-gate		tb.addr.buf = 0;
3097c478bdstevel@tonic-gate		use_any = TRUE;
31045916cdjpk		gzone = (getzoneid() == GLOBAL_ZONEID);
3117c478bdstevel@tonic-gate	} else if (netdir_getbyname(nconf, hs, &addrlist) != 0) {
3127c478bdstevel@tonic-gate
3137c478bdstevel@tonic-gate		syslog(LOG_ERR,
3147c478bdstevel@tonic-gate		"Cannot get address for transport %s host %s service %s",
315e810a98vv		    nconf->nc_netid, hs->h_host, hs->h_serv);
3167c478bdstevel@tonic-gate		(void) t_close(fd);
3177c478bdstevel@tonic-gate		return (-1);
3187c478bdstevel@tonic-gate	}
3197c478bdstevel@tonic-gate
3207c478bdstevel@tonic-gate	if (strcmp(nconf->nc_proto, "tcp") == 0) {
3217c478bdstevel@tonic-gate		/*
3227c478bdstevel@tonic-gate		 * If we're running over TCP, then set the
3237c478bdstevel@tonic-gate		 * SO_REUSEADDR option so that we can bind
3247c478bdstevel@tonic-gate		 * to our preferred address even if previously
3257c478bdstevel@tonic-gate		 * left connections exist in FIN_WAIT states.
3267c478bdstevel@tonic-gate		 * This is somewhat bogus, but otherwise you have
3277c478bdstevel@tonic-gate		 * to wait 2 minutes to restart after killing it.
3287c478bdstevel@tonic-gate		 */
3297c478bdstevel@tonic-gate		if (reuseaddr(fd) == -1) {
3307c478bdstevel@tonic-gate			syslog(LOG_WARNING,
3317c478bdstevel@tonic-gate			"couldn't set SO_REUSEADDR option on transport");
3327c478bdstevel@tonic-gate		}
33345916cdjpk	} else if (strcmp(nconf->nc_proto, "udp") == 0) {
33445916cdjpk		/*
33545916cdjpk		 * In order to run MLP on UDP, we need to handle creds.
33645916cdjpk		 */
33745916cdjpk		if (recvucred(fd) == -1) {
33845916cdjpk			syslog(LOG_WARNING,
33945916cdjpk			    "couldn't set SO_RECVUCRED option on transport");
34045916cdjpk		}
34145916cdjpk	}
34245916cdjpk
34345916cdjpk	/*
34445916cdjpk	 * Make non global zone nfs4_callback port MLP
34545916cdjpk	 */
34645916cdjpk	if (use_any && is_system_labeled() && !gzone) {
34745916cdjpk		if (anonmlp(fd) == -1) {
34845916cdjpk			/*
34945916cdjpk			 * failing to set this option means nfs4_callback
35045916cdjpk			 * could fail silently later. So fail it with
35145916cdjpk			 * with an error message now.
35245916cdjpk			 */
35345916cdjpk			syslog(LOG_ERR,
35445916cdjpk			    "couldn't set SO_ANON_MLP option on transport");
35545916cdjpk			(void) t_close(fd);
35645916cdjpk			return (-1);
35745916cdjpk		}
3587c478bdstevel@tonic-gate	}
3597c478bdstevel@tonic-gate
3607c478bdstevel@tonic-gate	if (nconf->nc_semantics == NC_TPI_CLTS)
3617c478bdstevel@tonic-gate		tb.qlen = 0;
3627c478bdstevel@tonic-gate	else
3637c478bdstevel@tonic-gate		tb.qlen = backlog;
3647c478bdstevel@tonic-gate
3657c478bdstevel@tonic-gate	/* LINTED pointer alignment */
3667c478bdstevel@tonic-gate	ntb = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL);
3677c478bdstevel@tonic-gate	if (ntb == (struct t_bind *)NULL) {
3687c478bdstevel@tonic-gate		syslog(LOG_ERR, "t_alloc failed:  t_errno %d, %m", t_errno);
3697c478bdstevel@tonic-gate		(void) t_close(fd);
3707c478bdstevel@tonic-gate		netdir_free((void *)addrlist, ND_ADDRLIST);
3717c478bdstevel@tonic-gate		return (-1);
3727c478bdstevel@tonic-gate	}
3737c478bdstevel@tonic-gate
3747c478bdstevel@tonic-gate	/*
3757c478bdstevel@tonic-gate	 * XXX - what about the space tb->addr.buf points to? This should
3767c478bdstevel@tonic-gate	 * be either a memcpy() to/from the buf fields, or t_alloc(fd,T_BIND,)
3777c478bdstevel@tonic-gate	 * should't be called with T_ALL.
3787c478bdstevel@tonic-gate	 */
3797c478bdstevel@tonic-gate	if (addrlist)
3807c478bdstevel@tonic-gate		tb.addr = *(addrlist->n_addrs);		/* structure copy */
3817c478bdstevel@tonic-gate
3827c478bdstevel@tonic-gate	if (t_bind(fd, &tb, ntb) == -1) {
3837c478bdstevel@tonic-gate		syslog(LOG_ERR, "t_bind failed:  t_errno %d, %m", t_errno);
3847c478bdstevel@tonic-gate		(void) t_free((char *)ntb, T_BIND);
3857c478bdstevel@tonic-gate		netdir_free((void *)addrlist, ND_ADDRLIST);
3867c478bdstevel@tonic-gate		(void) t_close(fd);
3877c478bdstevel@tonic-gate		return (-1);
3887c478bdstevel@tonic-gate	}
3897c478bdstevel@tonic-gate
3907c478bdstevel@tonic-gate	/* make sure we bound to the right address */
3917c478bdstevel@tonic-gate	if (use_any == FALSE &&
3927c478bdstevel@tonic-gate	    (tb.addr.len != ntb->addr.len ||
3937c478bdstevel@tonic-gate	    memcmp(tb.addr.buf, ntb->addr.buf, tb.addr.len) != 0)) {
3947c478bdstevel@tonic-gate		syslog(LOG_ERR, "t_bind to wrong address");
3957c478bdstevel@tonic-gate		(void) t_free((char *)ntb, T_BIND);
3967c478bdstevel@tonic-gate		netdir_free((void *)addrlist, ND_ADDRLIST);
3977c478bdstevel@tonic-gate		(void) t_close(fd);
3987c478bdstevel@tonic-gate		return (-1);
3997c478bdstevel@tonic-gate	}
4007c478bdstevel@tonic-gate
4017c478bdstevel@tonic-gate	/*
4027c478bdstevel@tonic-gate	 * Call nfs4svc_setport so that the kernel can be
4037c478bdstevel@tonic-gate	 * informed what port number the daemon is listing
4047c478bdstevel@tonic-gate	 * for incoming connection requests.
4057c478bdstevel@tonic-gate	 */
4067c478bdstevel@tonic-gate
4077c478bdstevel@tonic-gate	if ((nconf->nc_semantics == NC_TPI_COTS ||
4087c478bdstevel@tonic-gate	    nconf->nc_semantics == NC_TPI_COTS_ORD) && Mysvc4 != NULL)
4097c478bdstevel@tonic-gate		(*Mysvc4)(fd, NULL, nconf, NFS4_SETPORT, &ntb->addr);
4107c478bdstevel@tonic-gate
4117c478bdstevel@tonic-gate	*addr = &ntb->addr;
4127c478bdstevel@tonic-gate	netdir_free((void *)addrlist, ND_ADDRLIST);
4137c478bdstevel@tonic-gate
4147c478bdstevel@tonic-gate	if (strcmp(nconf->nc_proto, "tcp") == 0) {
4157c478bdstevel@tonic-gate		/*
4167c478bdstevel@tonic-gate		 * Disable the Nagle algorithm on TCP connections.
4177c478bdstevel@tonic-gate		 * Connections accepted from this listener will
4187c478bdstevel@tonic-gate		 * inherit the listener options.
4197c478bdstevel@tonic-gate		 */
4207c478bdstevel@tonic-gate
4217c478bdstevel@tonic-gate		/* LINTED pointer alignment */
4227c478bdstevel@tonic-gate		opt = (struct opthdr *)reqbuf;
4237c478bdstevel@tonic-gate		opt->level = IPPROTO_TCP;
4247c478bdstevel@tonic-gate		opt->name = TCP_NODELAY;
4257c478bdstevel@tonic-gate		opt->len = sizeof (int);
4267c478bdstevel@tonic-gate
4277c478bdstevel@tonic-gate		/* LINTED pointer alignment */
4287c478bdstevel@tonic-gate		*(int *)((char *)opt + sizeof (*opt)) = 1;
4297c478bdstevel@tonic-gate
4307c478bdstevel@tonic-gate		req.flags = T_NEGOTIATE;
4317c478bdstevel@tonic-gate		req.opt.len = sizeof (*opt) + opt->len;
4327c478bdstevel@tonic-gate		req.opt.buf = (char *)opt;
4337c478bdstevel@tonic-gate		resp.flags = 0;
4347c478bdstevel@tonic-gate		resp.opt.buf = reqbuf;
4357c478bdstevel@tonic-gate		resp.opt.maxlen = sizeof (reqbuf);
4367c478bdstevel@tonic-gate
4377c478bdstevel@tonic-gate		if (t_optmgmt(fd, &req, &resp) < 0 ||
438e810a98vv		    resp.flags != T_SUCCESS) {
4397c478bdstevel@tonic-gate			syslog(LOG_ERR,
4407c478bdstevel@tonic-gate	"couldn't set NODELAY option for proto %s: t_errno = %d, %m",
441e810a98vv			    nconf->nc_proto, t_errno);
4427c478bdstevel@tonic-gate		}
4432081ac1Dai Ngo
4442081ac1Dai Ngo		nfslib_set_sockbuf(fd);
4457c478bdstevel@tonic-gate	}
4467c478bdstevel@tonic-gate
4477c478bdstevel@tonic-gate	return (fd);
4487c478bdstevel@tonic-gate}
4497c478bdstevel@tonic-gate
4507c478bdstevel@tonic-gatestatic int
4512081ac1Dai Ngoget_opt(int fd, int level, int name)
4522081ac1Dai Ngo{
4532081ac1Dai Ngo	struct t_optmgmt req, res;
4542081ac1Dai Ngo	struct {
4552081ac1Dai Ngo		struct opthdr opt;
4562081ac1Dai Ngo		int value;
4572081ac1Dai Ngo	} reqbuf;
4582081ac1Dai Ngo
4592081ac1Dai Ngo	reqbuf.opt.level = level;
4602081ac1Dai Ngo	reqbuf.opt.name = name;
4612081ac1Dai Ngo	reqbuf.opt.len = sizeof (int);
4622081ac1Dai Ngo	reqbuf.value = 0;
4632081ac1Dai Ngo
4642081ac1Dai Ngo	req.flags = T_CURRENT;
4652081ac1Dai Ngo	req.opt.len = sizeof (reqbuf);
4662081ac1Dai Ngo	req.opt.buf = (char *)&reqbuf;
4672081ac1Dai Ngo
4682081ac1Dai Ngo	res.flags = 0;
4692081ac1Dai Ngo	res.opt.buf = (char *)&reqbuf;
4702081ac1Dai Ngo	res.opt.maxlen = sizeof (reqbuf);
4712081ac1Dai Ngo
4722081ac1Dai Ngo	if (t_optmgmt(fd, &req, &res) < 0 || res.flags != T_SUCCESS) {
4732081ac1Dai Ngo		t_error("t_optmgmt");
4742081ac1Dai Ngo		return (-1);
4752081ac1Dai Ngo	}
4762081ac1Dai Ngo	return (reqbuf.value);
4772081ac1Dai Ngo}
4782081ac1Dai Ngo
4792081ac1Dai Ngostatic int
48045916cdjpksetopt(int fd, int level, int name, int value)
4817c478bdstevel@tonic-gate{
4827c478bdstevel@tonic-gate	struct t_optmgmt req, resp;
48345916cdjpk	struct {
48445916cdjpk		struct opthdr opt;
48545916cdjpk		int value;
48645916cdjpk	} reqbuf;
4877c478bdstevel@tonic-gate
48845916cdjpk	reqbuf.opt.level = level;
48945916cdjpk	reqbuf.opt.name = name;
49045916cdjpk	reqbuf.opt.len = sizeof (int);
4917c478bdstevel@tonic-gate
49245916cdjpk	reqbuf.value = value;
4937c478bdstevel@tonic-gate
4947c478bdstevel@tonic-gate	req.flags = T_NEGOTIATE;
49545916cdjpk	req.opt.len = sizeof (reqbuf);
49645916cdjpk	req.opt.buf = (char *)&reqbuf;
4977c478bdstevel@tonic-gate
4987c478bdstevel@tonic-gate	resp.flags = 0;
49945916cdjpk	resp.opt.buf = (char *)&reqbuf;
5007c478bdstevel@tonic-gate	resp.opt.maxlen = sizeof (reqbuf);
5017c478bdstevel@tonic-gate
5027c478bdstevel@tonic-gate	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
5037c478bdstevel@tonic-gate		t_error("t_optmgmt");
5047c478bdstevel@tonic-gate		return (-1);
5057c478bdstevel@tonic-gate	}
5067c478bdstevel@tonic-gate	return (0);
5077c478bdstevel@tonic-gate}
5087c478bdstevel@tonic-gate
50945916cdjpkstatic int
51045916cdjpkreuseaddr(int fd)
51145916cdjpk{
51245916cdjpk	return (setopt(fd, SOL_SOCKET, SO_REUSEADDR, 1));
51345916cdjpk}
51445916cdjpk
51545916cdjpkstatic int
51645916cdjpkrecvucred(int fd)
51745916cdjpk{
51845916cdjpk	return (setopt(fd, SOL_SOCKET, SO_RECVUCRED, 1));
51945916cdjpk}
52045916cdjpk
52145916cdjpkstatic int
52245916cdjpkanonmlp(int fd)
52345916cdjpk{
52445916cdjpk	return (setopt(fd, SOL_SOCKET, SO_ANON_MLP, 1));
52545916cdjpk}
52645916cdjpk
5277c478bdstevel@tonic-gatevoid
5287c478bdstevel@tonic-gatenfslib_log_tli_error(char *tli_name, int fd, struct netconfig *nconf)
5297c478bdstevel@tonic-gate{
5307c478bdstevel@tonic-gate	int error;
5317c478bdstevel@tonic-gate
5327c478bdstevel@tonic-gate	/*
5337c478bdstevel@tonic-gate	 * Save the error code across syslog(), just in case syslog()
5347c478bdstevel@tonic-gate	 * gets its own error and, therefore, overwrites errno.
5357c478bdstevel@tonic-gate	 */
5367c478bdstevel@tonic-gate	error = errno;
5377c478bdstevel@tonic-gate	if (t_errno == TSYSERR) {
5387c478bdstevel@tonic-gate		syslog(LOG_ERR, "%s(file descriptor %d/transport %s) %m",
539e810a98vv		    tli_name, fd, nconf->nc_proto);
5407c478bdstevel@tonic-gate	} else {
5417c478bdstevel@tonic-gate		syslog(LOG_ERR,
542e810a98vv		    "%s(file descriptor %d/transport %s) TLI error %d",
543e810a98vv		    tli_name, fd, nconf->nc_proto, t_errno);
5447c478bdstevel@tonic-gate	}
5457c478bdstevel@tonic-gate	errno = error;
5467c478bdstevel@tonic-gate}
5477c478bdstevel@tonic-gate
5487c478bdstevel@tonic-gate/*
5497c478bdstevel@tonic-gate * Called to set up service over a particular transport.
5507c478bdstevel@tonic-gate */
5517c478bdstevel@tonic-gatevoid
5527c478bdstevel@tonic-gatedo_one(char *provider, NETSELDECL(proto), struct protob *protobp0,
5539ff75adSurya Prakki	int (*svc)(int, struct netbuf, struct netconfig *))
5547c478bdstevel@tonic-gate{
5557c478bdstevel@tonic-gate	register int sock;
5567c478bdstevel@tonic-gate	struct protob *protobp;
5577c478bdstevel@tonic-gate	struct netbuf *retaddr;
5587c478bdstevel@tonic-gate	struct netconfig *retnconf;
5597c478bdstevel@tonic-gate	struct netbuf addrmask;
5607c478bdstevel@tonic-gate	int vers;
5617c478bdstevel@tonic-gate	int err;
5627c478bdstevel@tonic-gate	int l;
5637c478bdstevel@tonic-gate
5647c478bdstevel@tonic-gate	if (provider)
5657c478bdstevel@tonic-gate		sock = bind_to_provider(provider, protobp0->serv, &retaddr,
566e810a98vv		    &retnconf);
5677c478bdstevel@tonic-gate	else
5687c478bdstevel@tonic-gate		sock = bind_to_proto(proto, protobp0->serv, &retaddr,
569e810a98vv		    &retnconf);
5707c478bdstevel@tonic-gate
5717c478bdstevel@tonic-gate	if (sock == -1) {
5727c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
5737c478bdstevel@tonic-gate	"Cannot establish %s service over %s: transport setup problem.",
574e810a98vv		    protobp0->serv, provider ? provider : proto);
5757c478bdstevel@tonic-gate		return;
5767c478bdstevel@tonic-gate	}
5777c478bdstevel@tonic-gate
5787c478bdstevel@tonic-gate	if (set_addrmask(sock, retnconf, &addrmask) < 0) {
5797c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
5807c478bdstevel@tonic-gate		    "Cannot set address mask for %s", retnconf->nc_netid);
5817c478bdstevel@tonic-gate		return;
5827c478bdstevel@tonic-gate	}
5837c478bdstevel@tonic-gate
5847c478bdstevel@tonic-gate	/*
5857c478bdstevel@tonic-gate	 * Register all versions of the programs in the protocol block list.
5867c478bdstevel@tonic-gate	 */
5877c478bdstevel@tonic-gate	l = strlen(NC_UDP);
5887c478bdstevel@tonic-gate	for (protobp = protobp0; protobp; protobp = protobp->next) {
5897c478bdstevel@tonic-gate		for (vers = protobp->versmin; vers <= protobp->versmax;
590e810a98vv		    vers++) {
5917c478bdstevel@tonic-gate			if ((protobp->program == NFS_PROGRAM ||
592e810a98vv			    protobp->program == NFS_ACL_PROGRAM) &&
593e810a98vv			    vers == NFS_V4 &&
594e810a98vv			    strncasecmp(retnconf->nc_proto, NC_UDP, l) == 0)
5957c478bdstevel@tonic-gate				continue;
5967c478bdstevel@tonic-gate
5979ff75adSurya Prakki			(void) rpcb_unset(protobp->program, vers, retnconf);
5989ff75adSurya Prakki			(void) rpcb_set(protobp->program, vers, retnconf,
5999ff75adSurya Prakki			    retaddr);
6007c478bdstevel@tonic-gate		}
6017c478bdstevel@tonic-gate	}
6027c478bdstevel@tonic-gate
6034a03041Dan Kruchinin	/*
6044a03041Dan Kruchinin	 * Register services with CLTS semantics right now.
6054a03041Dan Kruchinin	 * Note: services with COTS/COTS_ORD semantics will be
6064a03041Dan Kruchinin	 * registered later from cots_listen_event function.
6074a03041Dan Kruchinin	 */
6087c478bdstevel@tonic-gate	if (retnconf->nc_semantics == NC_TPI_CLTS) {
6097c478bdstevel@tonic-gate		/* Don't drop core if supporting module(s) aren't loaded. */
6107c478bdstevel@tonic-gate		(void) signal(SIGSYS, SIG_IGN);
6117c478bdstevel@tonic-gate
6127c478bdstevel@tonic-gate		/*
6137c478bdstevel@tonic-gate		 * svc() doesn't block, it returns success or failure.
6147c478bdstevel@tonic-gate		 */
6157c478bdstevel@tonic-gate
6167c478bdstevel@tonic-gate		if (svc == NULL && Mysvc4 != NULL)
6177c478bdstevel@tonic-gate			err = (*Mysvc4)(sock, &addrmask, retnconf,
618e810a98vv			    NFS4_SETPORT|NFS4_KRPC_START, retaddr);
6197c478bdstevel@tonic-gate		else
6207c478bdstevel@tonic-gate			err = (*svc)(sock, addrmask, retnconf);
6217c478bdstevel@tonic-gate
6227c478bdstevel@tonic-gate		if (err < 0) {
6237c478bdstevel@tonic-gate			(void) syslog(LOG_ERR,
624e810a98vv			    "Cannot establish %s service over <file desc."
625e810a98vv			    " %d, protocol %s> : %m. Exiting",
626e810a98vv			    protobp0->serv, sock, retnconf->nc_proto);
6277c478bdstevel@tonic-gate			exit(1);
6287c478bdstevel@tonic-gate		}
6297c478bdstevel@tonic-gate	}
6304a03041Dan Kruchinin	free(addrmask.buf);
6317c478bdstevel@tonic-gate
6327c478bdstevel@tonic-gate	/*
6337c478bdstevel@tonic-gate	 * We successfully set up the server over this transport.
6347c478bdstevel@tonic-gate	 * Add this descriptor to the one being polled on.
6357c478bdstevel@tonic-gate	 */
6367c478bdstevel@tonic-gate	add_to_poll_list(sock, retnconf);
6377c478bdstevel@tonic-gate}
6382081ac1Dai Ngo
6397c478bdstevel@tonic-gate/*
6407c478bdstevel@tonic-gate * Set up the NFS service over all the available transports.
6417c478bdstevel@tonic-gate * Returns -1 for failure, 0 for success.
6427c478bdstevel@tonic-gate */
6437c478bdstevel@tonic-gateint
6447c478bdstevel@tonic-gatedo_all(struct protob *protobp,
6459ff75adSurya Prakki	int (*svc)(int, struct netbuf, struct netconfig *))
6467c478bdstevel@tonic-gate{
6477c478bdstevel@tonic-gate	struct netconfig *nconf;
6487c478bdstevel@tonic-gate	NCONF_HANDLE *nc;
6497c478bdstevel@tonic-gate	int l;
6507c478bdstevel@tonic-gate
6517c478bdstevel@tonic-gate	if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
6527c478bdstevel@tonic-gate		syslog(LOG_ERR, "setnetconfig failed: %m");
6537c478bdstevel@tonic-gate		return (-1);
6547c478bdstevel@tonic-gate	}
6557c478bdstevel@tonic-gate	l = strlen(NC_UDP);
6567c478bdstevel@tonic-gate	while (nconf = getnetconfig(nc)) {
6577c478bdstevel@tonic-gate		if ((nconf->nc_flag & NC_VISIBLE) &&
6587c478bdstevel@tonic-gate		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0 &&
6597c478bdstevel@tonic-gate		    OK_TPI_TYPE(nconf) &&
6607c478bdstevel@tonic-gate		    (protobp->program != NFS4_CALLBACK ||
6617c478bdstevel@tonic-gate		    strncasecmp(nconf->nc_proto, NC_UDP, l) != 0))
6627c478bdstevel@tonic-gate			do_one(nconf->nc_device, nconf->nc_proto,
6639ff75adSurya Prakki			    protobp, svc);
6647c478bdstevel@tonic-gate	}
6657c478bdstevel@tonic-gate	(void) endnetconfig(nc);
6667c478bdstevel@tonic-gate	return (0);
6677c478bdstevel@tonic-gate}
6687c478bdstevel@tonic-gate
6697c478bdstevel@tonic-gate/*
6707c478bdstevel@tonic-gate * poll on the open transport descriptors for events and errors.
6717c478bdstevel@tonic-gate */
6727c478bdstevel@tonic-gatevoid
6737c478bdstevel@tonic-gatepoll_for_action(void)
6747c478bdstevel@tonic-gate{
6757c478bdstevel@tonic-gate	int nfds;
6767c478bdstevel@tonic-gate	int i;
6777c478bdstevel@tonic-gate
6787c478bdstevel@tonic-gate	/*
6797c478bdstevel@tonic-gate	 * Keep polling until all transports have been closed. When this
6807c478bdstevel@tonic-gate	 * happens, we return.
6817c478bdstevel@tonic-gate	 */
6827c478bdstevel@tonic-gate	while ((int)num_fds > 0) {
6837c478bdstevel@tonic-gate		nfds = poll(poll_array, num_fds, INFTIM);
6847c478bdstevel@tonic-gate		switch (nfds) {
6857c478bdstevel@tonic-gate		case 0:
6867c478bdstevel@tonic-gate			continue;
6877c478bdstevel@tonic-gate
6887c478bdstevel@tonic-gate		case -1:
6897c478bdstevel@tonic-gate			/*
6907c478bdstevel@tonic-gate			 * Some errors from poll could be
6917c478bdstevel@tonic-gate			 * due to temporary conditions, and we try to
6927c478bdstevel@tonic-gate			 * be robust in the face of them. Other
6937c478bdstevel@tonic-gate			 * errors (should never happen in theory)
6947c478bdstevel@tonic-gate			 * are fatal (eg. EINVAL, EFAULT).
6957c478bdstevel@tonic-gate			 */
6967c478bdstevel@tonic-gate			switch (errno) {
6977c478bdstevel@tonic-gate			case EINTR:
698e810a98vv				continue;
6997c478bdstevel@tonic-gate
7007c478bdstevel@tonic-gate			case EAGAIN:
7017c478bdstevel@tonic-gate			case ENOMEM:
7027c478bdstevel@tonic-gate				(void) sleep(10);
7037c478bdstevel@tonic-gate				continue;
7047c478bdstevel@tonic-gate
7057c478bdstevel@tonic-gate			default:
7067c478bdstevel@tonic-gate				(void) syslog(LOG_ERR,
707e810a98vv				    "poll failed: %m. Exiting");
7087c478bdstevel@tonic-gate				exit(1);
7097c478bdstevel@tonic-gate			}
7107c478bdstevel@tonic-gate		default:
7117c478bdstevel@tonic-gate			break;
7127c478bdstevel@tonic-gate		}
7137c478bdstevel@tonic-gate
7147c478bdstevel@tonic-gate		/*
7157c478bdstevel@tonic-gate		 * Go through the poll list looking for events.
7167c478bdstevel@tonic-gate		 */
7177c478bdstevel@tonic-gate		for (i = 0; i < num_fds && nfds > 0; i++) {
7187c478bdstevel@tonic-gate			if (poll_array[i].revents) {
7197c478bdstevel@tonic-gate				nfds--;
7207c478bdstevel@tonic-gate				/*
7217c478bdstevel@tonic-gate				 * We have a message, so try to read it.
7227c478bdstevel@tonic-gate				 * Record the error return in errno,
7237c478bdstevel@tonic-gate				 * so that syslog(LOG_ERR, "...%m")
7247c478bdstevel@tonic-gate				 * dumps the corresponding error string.
7257c478bdstevel@tonic-gate				 */
7267c478bdstevel@tonic-gate				if (conn_polled[i].nc.nc_semantics ==
7277c478bdstevel@tonic-gate				    NC_TPI_CLTS) {
7287c478bdstevel@tonic-gate					errno = do_poll_clts_action(
729e810a98vv					    poll_array[i].fd, i);
7307c478bdstevel@tonic-gate				} else {
7317c478bdstevel@tonic-gate					errno = do_poll_cots_action(
732e810a98vv					    poll_array[i].fd, i);
7337c478bdstevel@tonic-gate				}
7347c478bdstevel@tonic-gate
7357c478bdstevel@tonic-gate				if (errno == 0)
7367c478bdstevel@tonic-gate					continue;
7377c478bdstevel@tonic-gate				/*
7387c478bdstevel@tonic-gate				 * Most returned error codes mean that there is
7397c478bdstevel@tonic-gate				 * fatal condition which we can only deal with
7407c478bdstevel@tonic-gate				 * by closing the transport.
7417c478bdstevel@tonic-gate				 */
7427c478bdstevel@tonic-gate				if (errno != EAGAIN && errno != ENOMEM) {
7437c478bdstevel@tonic-gate					(void) syslog(LOG_ERR,
7447c478bdstevel@tonic-gate		"Error (%m) reading descriptor %d/transport %s. Closing it.",
745e810a98vv					    poll_array[i].fd,
746e810a98vv					    conn_polled[i].nc.nc_proto);
7477c478bdstevel@tonic-gate					(void) t_close(poll_array[i].fd);
7487c478bdstevel@tonic-gate					remove_from_poll_list(poll_array[i].fd);
7497c478bdstevel@tonic-gate
7507c478bdstevel@tonic-gate				} else if (errno == ENOMEM)
7517c478bdstevel@tonic-gate					(void) sleep(5);
7527c478bdstevel@tonic-gate			}
7537c478bdstevel@tonic-gate		}
7547c478bdstevel@tonic-gate	}
7557c478bdstevel@tonic-gate
7567c478bdstevel@tonic-gate	(void) syslog(LOG_ERR,
757e810a98vv	    "All transports have been closed with errors. Exiting.");
7587c478bdstevel@tonic-gate}
7597c478bdstevel@tonic-gate
7607c478bdstevel@tonic-gate/*
7617c478bdstevel@tonic-gate * Allocate poll/transport array entries for this descriptor.
7627c478bdstevel@tonic-gate */
7637c478bdstevel@tonic-gatestatic void
7647c478bdstevel@tonic-gateadd_to_poll_list(int fd, struct netconfig *nconf)
7657c478bdstevel@tonic-gate{
7667c478bdstevel@tonic-gate	static int poll_array_size = 0;
7677c478bdstevel@tonic-gate
7687c478bdstevel@tonic-gate	/*
7697c478bdstevel@tonic-gate	 * If the arrays are full, allocate new ones.
7707c478bdstevel@tonic-gate	 */
7717c478bdstevel@tonic-gate	if (num_fds == poll_array_size) {
7727c478bdstevel@tonic-gate		struct pollfd *tpa;
7737c478bdstevel@tonic-gate		struct conn_entry *tnp;
7747c478bdstevel@tonic-gate
7757c478bdstevel@tonic-gate		if (poll_array_size != 0) {
7767c478bdstevel@tonic-gate			tpa = poll_array;
7777c478bdstevel@tonic-gate			tnp = conn_polled;
7787c478bdstevel@tonic-gate		} else
7797c478bdstevel@tonic-gate			tpa = (struct pollfd *)0;
7807c478bdstevel@tonic-gate
7817c478bdstevel@tonic-gate		poll_array_size += POLL_ARRAY_INC_SIZE;
7827c478bdstevel@tonic-gate		/*
7837c478bdstevel@tonic-gate		 * Allocate new arrays.
7847c478bdstevel@tonic-gate		 */
7857c478bdstevel@tonic-gate		poll_array = (struct pollfd *)
7867c478bdstevel@tonic-gate		    malloc(poll_array_size * sizeof (struct pollfd) + 256);
7877c478bdstevel@tonic-gate		conn_polled = (struct conn_entry *)
7887c478bdstevel@tonic-gate		    malloc(poll_array_size * sizeof (struct conn_entry) + 256);
7897c478bdstevel@tonic-gate		if (poll_array == (struct pollfd *)NULL ||
7907c478bdstevel@tonic-gate		    conn_polled == (struct conn_entry *)NULL) {
7917c478bdstevel@tonic-gate			syslog(LOG_ERR, "malloc failed for poll array");
7927c478bdstevel@tonic-gate			exit(1);
7937c478bdstevel@tonic-gate		}
7947c478bdstevel@tonic-gate
7957c478bdstevel@tonic-gate		/*
7967c478bdstevel@tonic-gate		 * Copy the data of the old ones into new arrays, and
7977c478bdstevel@tonic-gate		 * free the old ones.
7987c478bdstevel@tonic-gate		 */
7997c478bdstevel@tonic-gate		if (tpa) {
8007c478bdstevel@tonic-gate			(void) memcpy((void *)poll_array, (void *)tpa,
801e810a98vv			    num_fds * sizeof (struct pollfd));
8027c478bdstevel@tonic-gate			(void) memcpy((void *)conn_polled, (void *)tnp,
803e810a98vv			    num_fds * sizeof (struct conn_entry));
8047c478bdstevel@tonic-gate			free((void *)tpa);
8057c478bdstevel@tonic-gate			free((void *)tnp);
8067c478bdstevel@tonic-gate		}
8077c478bdstevel@tonic-gate	}
8087c478bdstevel@tonic-gate
8097c478bdstevel@tonic-gate	/*
8107c478bdstevel@tonic-gate	 * Set the descriptor and event list. All possible events are
8117c478bdstevel@tonic-gate	 * polled for.
8127c478bdstevel@tonic-gate	 */
8137c478bdstevel@tonic-gate	poll_array[num_fds].fd = fd;
8147c478bdstevel@tonic-gate	poll_array[num_fds].events = POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI;
8157c478bdstevel@tonic-gate
8167c478bdstevel@tonic-gate	/*
8177c478bdstevel@tonic-gate	 * Copy the transport data over too.
8187c478bdstevel@tonic-gate	 */
8197c478bdstevel@tonic-gate	conn_polled[num_fds].nc = *nconf;
8207c478bdstevel@tonic-gate	conn_polled[num_fds].closing = 0;
8217c478bdstevel@tonic-gate
8227c478bdstevel@tonic-gate	/*
8237c478bdstevel@tonic-gate	 * Set the descriptor to non-blocking. Avoids a race
8247c478bdstevel@tonic-gate	 * between data arriving on the stream and then having it
8257c478bdstevel@tonic-gate	 * flushed before we can read it.
8267c478bdstevel@tonic-gate	 */
8277c478bdstevel@tonic-gate	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
8287c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
8297c478bdstevel@tonic-gate	"fcntl(file desc. %d/transport %s, F_SETFL, O_NONBLOCK): %m. Exiting",
830e810a98vv		    num_fds, nconf->nc_proto);
8317c478bdstevel@tonic-gate		exit(1);
8327c478bdstevel@tonic-gate	}
8337c478bdstevel@tonic-gate
8347c478bdstevel@tonic-gate	/*
8357c478bdstevel@tonic-gate	 * Count this descriptor.
8367c478bdstevel@tonic-gate	 */
8377c478bdstevel@tonic-gate	++num_fds;
8387c478bdstevel@tonic-gate}
8397c478bdstevel@tonic-gate
8407c478bdstevel@tonic-gatestatic void
8417c478bdstevel@tonic-gateremove_from_poll_list(int fd)
8427c478bdstevel@tonic-gate{
8437c478bdstevel@tonic-gate	int i;
8447c478bdstevel@tonic-gate	int num_to_copy;
8457c478bdstevel@tonic-gate
8467c478bdstevel@tonic-gate	for (i = 0; i < num_fds; i++) {
8477c478bdstevel@tonic-gate		if (poll_array[i].fd == fd) {
8487c478bdstevel@tonic-gate			--num_fds;
8497c478bdstevel@tonic-gate			num_to_copy = num_fds - i;
8507c478bdstevel@tonic-gate			(void) memcpy((void *)&poll_array[i],
851e810a98vv			    (void *)&poll_array[i+1],
852e810a98vv			    num_to_copy * sizeof (struct pollfd));
8537c478bdstevel@tonic-gate			(void) memset((void *)&poll_array[num_fds], 0,
854e810a98vv			    sizeof (struct pollfd));
8557c478bdstevel@tonic-gate			(void) memcpy((void *)&conn_polled[i],
856e810a98vv			    (void *)&conn_polled[i+1],
857e810a98vv			    num_to_copy * sizeof (struct conn_entry));
8587c478bdstevel@tonic-gate			(void) memset((void *)&conn_polled[num_fds], 0,
859e810a98vv			    sizeof (struct conn_entry));
8607c478bdstevel@tonic-gate			return;
8617c478bdstevel@tonic-gate		}
8627c478bdstevel@tonic-gate	}
8637c478bdstevel@tonic-gate	syslog(LOG_ERR, "attempt to remove nonexistent fd from poll list");
8647c478bdstevel@tonic-gate
8657c478bdstevel@tonic-gate}
8667c478bdstevel@tonic-gate
8677c478bdstevel@tonic-gate/*
8687c478bdstevel@tonic-gate * Called to read and interpret the event on a connectionless descriptor.
8697c478bdstevel@tonic-gate * Returns 0 if successful, or a UNIX error code if failure.
8707c478bdstevel@tonic-gate */
8717c478bdstevel@tonic-gatestatic int
8727c478bdstevel@tonic-gatedo_poll_clts_action(int fd, int conn_index)
8737c478bdstevel@tonic-gate{
8747c478bdstevel@tonic-gate	int error;
8757c478bdstevel@tonic-gate	int ret;
8767c478bdstevel@tonic-gate	int flags;
8777c478bdstevel@tonic-gate	struct netconfig *nconf = &conn_polled[conn_index].nc;
8787c478bdstevel@tonic-gate	static struct t_unitdata *unitdata = NULL;
8797c478bdstevel@tonic-gate	static struct t_uderr *uderr = NULL;
8807c478bdstevel@tonic-gate	static int oldfd = -1;
8817c478bdstevel@tonic-gate	struct nd_hostservlist *host = NULL;
8827c478bdstevel@tonic-gate	struct strbuf ctl[1], data[1];
8837c478bdstevel@tonic-gate	/*
8847c478bdstevel@tonic-gate	 * We just need to have some space to consume the
8857c478bdstevel@tonic-gate	 * message in the event we can't use the TLI interface to do the
8867c478bdstevel@tonic-gate	 * job.
8877c478bdstevel@tonic-gate	 *
8887c478bdstevel@tonic-gate	 * We flush the message using getmsg(). For the control part
8897c478bdstevel@tonic-gate	 * we allocate enough for any TPI header plus 32 bytes for address
8907c478bdstevel@tonic-gate	 * and options. For the data part, there is nothing magic about
8917c478bdstevel@tonic-gate	 * the size of the array, but 256 bytes is probably better than
8927c478bdstevel@tonic-gate	 * 1 byte, and we don't expect any data portion anyway.
8937c478bdstevel@tonic-gate	 *
8947c478bdstevel@tonic-gate	 * If the array sizes are too small, we handle this because getmsg()
8957c478bdstevel@tonic-gate	 * (called to consume the message) will return MOREDATA|MORECTL.
8967c478bdstevel@tonic-gate	 * Thus we just call getmsg() until it's read the message.
8977c478bdstevel@tonic-gate	 */
8987c478bdstevel@tonic-gate	char ctlbuf[sizeof (union T_primitives) + 32];
8997c478bdstevel@tonic-gate	char databuf[256];
9007c478bdstevel@tonic-gate
9017c478bdstevel@tonic-gate	/*
9027c478bdstevel@tonic-gate	 * If this is the same descriptor as the last time
9037c478bdstevel@tonic-gate	 * do_poll_clts_action was called, we can save some
9047c478bdstevel@tonic-gate	 * de-allocation and allocation.
9057c478bdstevel@tonic-gate	 */
9067c478bdstevel@tonic-gate	if (oldfd != fd) {
9077c478bdstevel@tonic-gate		oldfd = fd;
9087c478bdstevel@tonic-gate
9097c478bdstevel@tonic-gate		if (unitdata) {
9107c478bdstevel@tonic-gate			(void) t_free((char *)unitdata, T_UNITDATA);
9117c478bdstevel@tonic-gate			unitdata = NULL;
9127c478bdstevel@tonic-gate		}
9137c478bdstevel@tonic-gate		if (uderr) {
9147c478bdstevel@tonic-gate			(void) t_free((char *)uderr, T_UDERROR);
9157c478bdstevel@tonic-gate			uderr = NULL;
9167c478bdstevel@tonic-gate		}
9177c478bdstevel@tonic-gate	}
9187c478bdstevel@tonic-gate
9197c478bdstevel@tonic-gate	/*
9207c478bdstevel@tonic-gate	 * Allocate a unitdata structure for receiving the event.
9217c478bdstevel@tonic-gate	 */
9227c478bdstevel@tonic-gate	if (unitdata == NULL) {
9237c478bdstevel@tonic-gate		/* LINTED pointer alignment */
9247c478bdstevel@tonic-gate		unitdata = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL);
9257c478bdstevel@tonic-gate		if (unitdata == NULL) {
9267c478bdstevel@tonic-gate			if (t_errno == TSYSERR) {
9277c478bdstevel@tonic-gate				/*
9287c478bdstevel@tonic-gate				 * Save the error code across
9297c478bdstevel@tonic-gate				 * syslog(), just in case
9307c478bdstevel@tonic-gate				 * syslog() gets its own error
9317c478bdstevel@tonic-gate				 * and therefore overwrites errno.
9327c478bdstevel@tonic-gate				 */
9337c478bdstevel@tonic-gate				error = errno;
9347c478bdstevel@tonic-gate				(void) syslog(LOG_ERR,
9357c478bdstevel@tonic-gate	"t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed: %m",
936e810a98vv				    fd, nconf->nc_proto);
9377c478bdstevel@tonic-gate				return (error);
9387c478bdstevel@tonic-gate			}
9397c478bdstevel@tonic-gate			(void) syslog(LOG_ERR,
9407c478bdstevel@tonic-gate"t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed TLI error %d",
941e810a98vv			    fd, nconf->nc_proto, t_errno);
9427c478bdstevel@tonic-gate			goto flush_it;
9437c478bdstevel@tonic-gate		}
9447c478bdstevel@tonic-gate	}
9457c478bdstevel@tonic-gate
9467c478bdstevel@tonic-gatetry_again:
9477c478bdstevel@tonic-gate	flags = 0;
9487c478bdstevel@tonic-gate
9497c478bdstevel@tonic-gate	/*
9507c478bdstevel@tonic-gate	 * The idea is we wait for T_UNITDATA_IND's. Of course,
9517c478bdstevel@tonic-gate	 * we don't get any, because rpcmod filters them out.
9527c478bdstevel@tonic-gate	 * However, we need to call t_rcvudata() to let TLI
9537c478bdstevel@tonic-gate	 * tell us we have a T_UDERROR_IND.
9547c478bdstevel@tonic-gate	 *
9557c478bdstevel@tonic-gate	 * algorithm is:
9567c478bdstevel@tonic-gate	 * 	t_rcvudata(), expecting TLOOK.
9577c478bdstevel@tonic-gate	 * 	t_look(), expecting T_UDERR.
9587c478bdstevel@tonic-gate	 * 	t_rcvuderr(), expecting success (0).
9597c478bdstevel@tonic-gate	 * 	expand destination address into ASCII,
9607c478bdstevel@tonic-gate	 *	and dump it.
9617c478bdstevel@tonic-gate	 */
9627c478bdstevel@tonic-gate
9637c478bdstevel@tonic-gate	ret = t_rcvudata(fd, unitdata, &flags);
9647c478bdstevel@tonic-gate	if (ret == 0 || t_errno == TBUFOVFLW) {
9657c478bdstevel@tonic-gate		(void) syslog(LOG_WARNING,
9667c478bdstevel@tonic-gate"t_rcvudata(file descriptor %d/transport %s) got unexpected data, %d bytes",
967e810a98vv		    fd, nconf->nc_proto, unitdata->udata.len);
9687c478bdstevel@tonic-gate
9697c478bdstevel@tonic-gate		/*
9707c478bdstevel@tonic-gate		 * Even though we don't expect any data, in case we do,
9717c478bdstevel@tonic-gate		 * keep reading until there is no more.
9727c478bdstevel@tonic-gate		 */
9737c478bdstevel@tonic-gate		if (flags & T_MORE)
9747c478bdstevel@tonic-gate			goto try_again;
9757c478bdstevel@tonic-gate
9767c478bdstevel@tonic-gate		return (0);
9777c478bdstevel@tonic-gate	}
9787c478bdstevel@tonic-gate
9797c478bdstevel@tonic-gate	switch (t_errno) {
9807c478bdstevel@tonic-gate	case TNODATA:
9817c478bdstevel@tonic-gate		return (0);
9827c478bdstevel@tonic-gate	case TSYSERR:
9837c478bdstevel@tonic-gate		/*
9847c478bdstevel@tonic-gate		 * System errors are returned to caller.
9857c478bdstevel@tonic-gate		 * Save the error code across
9867c478bdstevel@tonic-gate		 * syslog(), just in case
9877c478bdstevel@tonic-gate		 * syslog() gets its own error
9887c478bdstevel@tonic-gate		 * and therefore overwrites errno.
9897c478bdstevel@tonic-gate		 */
9907c478bdstevel@tonic-gate		error = errno;
9917c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
992e810a98vv		    "t_rcvudata(file descriptor %d/transport %s) %m",
993e810a98vv		    fd, nconf->nc_proto);
9947c478bdstevel@tonic-gate		return (error);
9957c478bdstevel@tonic-gate	case TLOOK:
9967c478bdstevel@tonic-gate		break;
9977c478bdstevel@tonic-gate	default:
9987c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
9997c478bdstevel@tonic-gate		"t_rcvudata(file descriptor %d/transport %s) TLI error %d",
1000e810a98vv		    fd, nconf->nc_proto, t_errno);
10017c478bdstevel@tonic-gate		goto flush_it;
10027c478bdstevel@tonic-gate	}
10037c478bdstevel@tonic-gate
10047c478bdstevel@tonic-gate	ret = t_look(fd);
10057c478bdstevel@tonic-gate	switch (ret) {
10067c478bdstevel@tonic-gate	case 0:
10077c478bdstevel@tonic-gate		return (0);
10087c478bdstevel@tonic-gate	case -1:
10097c478bdstevel@tonic-gate		/*
10107c478bdstevel@tonic-gate		 * System errors are returned to caller.
10117c478bdstevel@tonic-gate		 */
10127c478bdstevel@tonic-gate		if (t_errno == TSYSERR) {
10137c478bdstevel@tonic-gate			/*
10147c478bdstevel@tonic-gate			 * Save the error code across
10157c478bdstevel@tonic-gate			 * syslog(), just in case
10167c478bdstevel@tonic-gate			 * syslog() gets its own error
10177c478bdstevel@tonic-gate			 * and therefore overwrites errno.
10187c478bdstevel@tonic-gate			 */
10197c478bdstevel@tonic-gate			error = errno;
10207c478bdstevel@tonic-gate			(void) syslog(LOG_ERR,
1021e810a98vv			    "t_look(file descriptor %d/transport %s) %m",
1022e810a98vv			    fd, nconf->nc_proto);
10237c478bdstevel@tonic-gate			return (error);
10247c478bdstevel@tonic-gate		}
10257c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
1026e810a98vv		    "t_look(file descriptor %d/transport %s) TLI error %d",
1027e810a98vv		    fd, nconf->nc_proto, t_errno);
10287c478bdstevel@tonic-gate		goto flush_it;
10297c478bdstevel@tonic-gate	case T_UDERR:
10307c478bdstevel@tonic-gate		break;
10317c478bdstevel@tonic-gate	default:
10327c478bdstevel@tonic-gate		(void) syslog(LOG_WARNING,
10337c478bdstevel@tonic-gate	"t_look(file descriptor %d/transport %s) returned %d not T_UDERR (%d)",
1034e810a98vv		    fd, nconf->nc_proto, ret, T_UDERR);
10357c478bdstevel@tonic-gate	}
10367c478bdstevel@tonic-gate
10377c478bdstevel@tonic-gate	if (uderr == NULL) {
10387c478bdstevel@tonic-gate		/* LINTED pointer alignment */
10397c478bdstevel@tonic-gate		uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL);
10407c478bdstevel@tonic-gate		if (uderr == NULL) {
10417c478bdstevel@tonic-gate			if (t_errno == TSYSERR) {
10427c478bdstevel@tonic-gate				/*
10437c478bdstevel@tonic-gate				 * Save the error code across
10447c478bdstevel@tonic-gate				 * syslog(), just in case
10457c478bdstevel@tonic-gate				 * syslog() gets its own error
10467c478bdstevel@tonic-gate				 * and therefore overwrites errno.
10477c478bdstevel@tonic-gate				 */
10487c478bdstevel@tonic-gate				error = errno;
10497c478bdstevel@tonic-gate				(void) syslog(LOG_ERR,
10507c478bdstevel@tonic-gate	"t_alloc(file descriptor %d/transport %s, T_UDERROR) failed: %m",
1051e810a98vv				    fd, nconf->nc_proto);
10527c478bdstevel@tonic-gate				return (error);
10537c478bdstevel@tonic-gate			}
10547c478bdstevel@tonic-gate			(void) syslog(LOG_ERR,
10557c478bdstevel@tonic-gate"t_alloc(file descriptor %d/transport %s, T_UDERROR) failed TLI error: %d",
1056e810a98vv			    fd, nconf->nc_proto, t_errno);
10577c478bdstevel@tonic-gate			goto flush_it;
10587c478bdstevel@tonic-gate		}
10597c478bdstevel@tonic-gate	}
10607c478bdstevel@tonic-gate
10617c478bdstevel@tonic-gate	ret = t_rcvuderr(fd, uderr);
10627c478bdstevel@tonic-gate	if (ret == 0) {
10637c478bdstevel@tonic-gate
10647c478bdstevel@tonic-gate		/*
10657c478bdstevel@tonic-gate		 * Save the datagram error in errno, so that the
10667c478bdstevel@tonic-gate		 * %m argument to syslog picks up the error string.
10677c478bdstevel@tonic-gate		 */
10687c478bdstevel@tonic-gate		errno = uderr->error;
10697c478bdstevel@tonic-gate
10707c478bdstevel@tonic-gate		/*
10717c478bdstevel@tonic-gate		 * Log the datagram error, then log the host that
10727c478bdstevel@tonic-gate		 * probably triggerred. Cannot log both in the
10737c478bdstevel@tonic-gate		 * same transaction because of packet size limitations
10747c478bdstevel@tonic-gate		 * in /dev/log.
10757c478bdstevel@tonic-gate		 */
10767c478bdstevel@tonic-gate		(void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
10777c478bdstevel@tonic-gate"NFS response over <file descriptor %d/transport %s> generated error: %m",
1078e810a98vv		    fd, nconf->nc_proto);
10797c478bdstevel@tonic-gate
10807c478bdstevel@tonic-gate		/*
10817c478bdstevel@tonic-gate		 * Try to map the client's address back to a
10827c478bdstevel@tonic-gate		 * name.
10837c478bdstevel@tonic-gate		 */
10847c478bdstevel@tonic-gate		ret = netdir_getbyaddr(nconf, &host, &uderr->addr);
10857c478bdstevel@tonic-gate		if (ret != -1 && host && host->h_cnt > 0 &&
10867c478bdstevel@tonic-gate		    host->h_hostservs) {
10877c478bdstevel@tonic-gate		(void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
10887c478bdstevel@tonic-gate"Bad NFS response was sent to client with host name: %s; service port: %s",
1089e810a98vv		    host->h_hostservs->h_host,
1090e810a98vv		    host->h_hostservs->h_serv);
10917c478bdstevel@tonic-gate		} else {
10927c478bdstevel@tonic-gate			int i, j;
10937c478bdstevel@tonic-gate			char *buf;
10947c478bdstevel@tonic-gate			char *hex = "0123456789abcdef";
10957c478bdstevel@tonic-gate
10967c478bdstevel@tonic-gate			/*
10977c478bdstevel@tonic-gate			 * Mapping failed, print the whole thing
10987c478bdstevel@tonic-gate			 * in ASCII hex.
10997c478bdstevel@tonic-gate			 */
11007c478bdstevel@tonic-gate			buf = (char *)malloc(uderr->addr.len * 2 + 1);
11017c478bdstevel@tonic-gate			for (i = 0, j = 0; i < uderr->addr.len; i++, j += 2) {
11027c478bdstevel@tonic-gate				buf[j] = hex[((uderr->addr.buf[i]) >> 4) & 0xf];
11037c478bdstevel@tonic-gate				buf[j+1] = hex[uderr->addr.buf[i] & 0xf];
11047c478bdstevel@tonic-gate			}
11057c478bdstevel@tonic-gate			buf[j] = '\0';
11067c478bdstevel@tonic-gate		(void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
11077c478bdstevel@tonic-gate	"Bad NFS response was sent to client with transport address: 0x%s",
1108e810a98vv		    buf);
11097c478bdstevel@tonic-gate			free((void *)buf);
11107c478bdstevel@tonic-gate		}
11117c478bdstevel@tonic-gate
11127c478bdstevel@tonic-gate		if (ret == 0 && host != NULL)
11137c478bdstevel@tonic-gate			netdir_free((void *)host, ND_HOSTSERVLIST);
11147c478bdstevel@tonic-gate		return (0);
11157c478bdstevel@tonic-gate	}
11167c478bdstevel@tonic-gate
11177c478bdstevel@tonic-gate	switch (t_errno) {
11187c478bdstevel@tonic-gate	case TNOUDERR:
11197c478bdstevel@tonic-gate		goto flush_it;
11207c478bdstevel@tonic-gate	case TSYSERR:
11217c478bdstevel@tonic-gate		/*
11227c478bdstevel@tonic-gate		 * System errors are returned to caller.
11237c478bdstevel@tonic-gate		 * Save the error code across
11247c478bdstevel@tonic-gate		 * syslog(), just in case
11257c478bdstevel@tonic-gate		 * syslog() gets its own error
11267c478bdstevel@tonic-gate		 * and therefore overwrites errno.
11277c478bdstevel@tonic-gate		 */
11287c478bdstevel@tonic-gate		error = errno;
11297c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
1130e810a98vv		    "t_rcvuderr(file descriptor %d/transport %s) %m",
1131e810a98vv		    fd, nconf->nc_proto);
11327c478bdstevel@tonic-gate		return (error);
11337c478bdstevel@tonic-gate	default:
11347c478bdstevel@tonic-gate		(void) syslog(LOG_ERR,
11357c478bdstevel@tonic-gate		"t_rcvuderr(file descriptor %d/transport %s) TLI error %d",
1136e810a98vv		    fd, nconf->nc_proto, t_errno);
11377c478bdstevel@tonic-gate		goto flush_it;
11387c478bdstevel@tonic-gate	}
11397c478bdstevel@tonic-gate
11407c478bdstevel@tonic-gateflush_it:
11417c478bdstevel@tonic-gate	/*
11427c478bdstevel@tonic-gate	 * If we get here, then we could not cope with whatever message
11437c478bdstevel@tonic-gate	 * we attempted to read, so flush it. If we did read a message,
11447c478bdstevel@tonic-gate	 * and one isn't present, that is all right, because fd is in
11457c478bdstevel@tonic-gate	 * nonblocking mode.
11467c478bdstevel@tonic-gate	 */
11477c478bdstevel@tonic-gate	(void) syslog(LOG_ERR,
11487c478bdstevel@tonic-gate	"Flushing one input message from <file descriptor %d/transport %s>",
1149e810a98vv	    fd, nconf->nc_proto);
11507c478bdstevel@tonic-gate
11517c478bdstevel@tonic-gate	/*
11527c478bdstevel@tonic-gate	 * Read and discard the message. Do this this until there is
11537c478bdstevel@tonic-gate	 * no more control/data in the message or until we get an error.
11547c478bdstevel@tonic-gate	 */
11557c478bdstevel@tonic-gate	do {
11567c478bdstevel@tonic-gate		ctl->maxlen = sizeof (ctlbuf);
11577c478bdstevel@tonic-gate		ctl->buf = ctlbuf;
11587c478bdstevel@tonic-gate		data->maxlen = sizeof (databuf);
11597c478bdstevel@tonic-gate		data->buf = databuf;
11607c478bdstevel@tonic-gate		flags = 0;
11617c478bdstevel@tonic-gate		ret = getmsg(fd, ctl, data, &flags);
11627c478bdstevel@tonic-gate		if (ret == -1)
11637c478bdstevel@tonic-gate			return (errno);
11647c478bdstevel@tonic-gate	} while (ret != 0);
11657c478bdstevel@tonic-gate
11667c478bdstevel@tonic-gate	return (0);
11677c478bdstevel@tonic-gate}
11687c478bdstevel@tonic-gate
11697c478bdstevel@tonic-gatestatic void
11707c478bdstevel@tonic-gateconn_close_oldest(void)
11717c478bdstevel@tonic-gate{
11727c478bdstevel@tonic-gate	int fd;
11737c478bdstevel@tonic-gate	int i1;
11747c478bdstevel@tonic-gate
11757c478bdstevel@tonic-gate	/*
11767c478bdstevel@tonic-gate	 * Find the oldest connection that is not already in the
11777c478bdstevel@tonic-gate	 * process of shutting down.
11787c478bdstevel@tonic-gate	 */
11797c478bdstevel@tonic-gate	for (i1 = end_listen_fds; /* no conditional expression */; i1++) {
11807c478bdstevel@tonic-gate		if (i1 >= num_fds)
11817c478bdstevel@tonic-gate			return;
11827c478bdstevel@tonic-gate		if (conn_polled[i1].closing == 0)
11837c478bdstevel@tonic-gate			break;
11847c478bdstevel@tonic-gate	}
11857c478bdstevel@tonic-gate#ifdef DEBUG
11867c478bdstevel@tonic-gate	printf("too many connections (%d), releasing oldest (%d)\n",
1187e810a98vv	    num_conns, poll_array[i1].fd);
11887c478bdstevel@tonic-gate#else
11897c478bdstevel@tonic-gate	syslog(LOG_WARNING, "too many connections (%d), releasing oldest (%d)",
1190e810a98vv	    num_conns, poll_array[i1].fd);
11917c478bdstevel@tonic-gate#endif
11927c478bdstevel@tonic-gate	fd = poll_array[i1].fd;
11937c478bdstevel@tonic-gate	if (conn_polled[i1].nc.nc_semantics == NC_TPI_COTS) {
11947c478bdstevel@tonic-gate		/*
11957c478bdstevel@tonic-gate		 * For politeness, send a T_DISCON_REQ to the transport
11967c478bdstevel@tonic-gate		 * provider.  We close the stream anyway.
11977c478bdstevel@tonic-gate		 */
11987c478bdstevel@tonic-gate		(void) t_snddis(fd, (struct t_call *)0);
11997c478bdstevel@tonic-gate		num_conns--;
12007c478bdstevel@tonic-gate		remove_from_poll_list(fd);
12017c478bdstevel@tonic-gate		(void) t_close(fd);
12027c478bdstevel@tonic-gate	} else {
12037c478bdstevel@tonic-gate		/*
12047c478bdstevel@tonic-gate		 * For orderly release, we do not close the stream
12057c478bdstevel@tonic-gate		 * until the T_ORDREL_IND arrives to complete
12067c478bdstevel@tonic-gate		 * the handshake.
12077c478bdstevel@tonic-gate		 */
12087c478bdstevel@tonic-gate		if (t_sndrel(fd) == 0)
12097c478bdstevel@tonic-gate			conn_polled[i1].closing = 1;
12107c478bdstevel@tonic-gate	}
12117c478bdstevel@tonic-gate}
12127c478bdstevel@tonic-gate
12137c478bdstevel@tonic-gatestatic boolean_t
12147c478bdstevel@tonic-gateconn_get(int fd, struct netconfig *nconf, struct conn_ind **connp)
12157c478bdstevel@tonic-gate{
12167c478bdstevel@tonic-gate	struct conn_ind	*conn;
12177c478bdstevel@tonic-gate	struct conn_ind	*next_conn;
12187c478bdstevel@tonic-gate
12197c478bdstevel@tonic-gate	conn = (struct conn_ind *)malloc(sizeof (*conn));
12207c478bdstevel@tonic-gate	if (conn == NULL) {
12217c478bdstevel@tonic-gate		syslog(LOG_ERR, "malloc for listen indication failed");
12227c478bdstevel@tonic-gate		return (FALSE);
12237c478bdstevel@tonic-gate	}
12247c478bdstevel@tonic-gate
12257c478bdstevel@tonic-gate	/* LINTED pointer alignment */
12267c478bdstevel@tonic-gate	conn->conn_call = (struct t_call *)t_alloc(fd, T_CALL, T_ALL);
12277c478bdstevel@tonic-gate	if (conn->conn_call == NULL) {
12287c478bdstevel@tonic-gate		free((char *)conn);
12297c478bdstevel@tonic-gate		nfslib_log_tli_error("t_alloc", fd, nconf);
12307c478bdstevel@tonic-gate		return (FALSE);
12317c478bdstevel@tonic-gate	}
12327c478bdstevel@tonic-gate
12337c478bdstevel@tonic-gate	if (t_listen(fd, conn->conn_call) == -1) {
12347c478bdstevel@tonic-gate		nfslib_log_tli_error("t_listen", fd, nconf);
12357c478bdstevel@tonic-gate		(void) t_free((char *)conn->conn_call, T_CALL);
12367c478bdstevel@tonic-gate		free((char *)conn);
12377c478bdstevel@tonic-gate		return (FALSE);
12387c478bdstevel@tonic-gate	}
12397c478bdstevel@tonic-gate
12407c478bdstevel@tonic-gate	if (conn->conn_call->udata.len > 0) {
12417c478bdstevel@tonic-gate		syslog(LOG_WARNING,
12427c478bdstevel@tonic-gate	"rejecting inbound connection(%s) with %d bytes of connect data",
1243e810a98vv		    nconf->nc_proto, conn->conn_call->udata.len);
12447c478bdstevel@tonic-gate
12457c478bdstevel@tonic-gate		conn->conn_call->udata.len = 0;
12467c478bdstevel@tonic-gate		(void) t_snddis(fd, conn->conn_call);
12477c478bdstevel@tonic-gate		(void) t_free((char *)conn->conn_call, T_CALL);
12487c478bdstevel@tonic-gate		free((char *)conn);
12497c478bdstevel@tonic-gate		return (FALSE);
12507c478bdstevel@tonic-gate	}
12517c478bdstevel@tonic-gate
12527c478bdstevel@tonic-gate	if ((next_conn = *connp) != NULL) {
12537c478bdstevel@tonic-gate		next_conn->conn_prev->conn_next = conn;
12547c478bdstevel@tonic-gate		conn->conn_next = next_conn;
12557c478bdstevel@tonic-gate		conn->conn_prev = next_conn->conn_prev;
12567c478bdstevel@tonic-gate		next_conn->conn_prev = conn;
12577c478bdstevel@tonic-gate	} else {
12587c478bdstevel@tonic-gate		conn->conn_next = conn;
12597c478bdstevel@tonic-gate		conn->conn_prev = conn;
12607c478bdstevel@tonic-gate		*connp = conn;
12617c478bdstevel@tonic-gate	}
12627c478bdstevel@tonic-gate	return (TRUE);
12637c478bdstevel@tonic-gate}
12647c478bdstevel@tonic-gate
12657c478bdstevel@tonic-gatestatic int
12667c478bdstevel@tonic-gatediscon_get(int fd, struct netconfig *nconf, struct conn_ind **connp)
12677c478bdstevel@tonic-gate{
12687c478bdstevel@tonic-gate	struct conn_ind	*conn;
12697c478bdstevel@tonic-gate	struct t_discon	discon;
12707c478bdstevel@tonic-gate
12717c478bdstevel@tonic-gate	discon.udata.buf = (char *)0;
12727c478bdstevel@tonic-gate	discon.udata.maxlen = 0;
12737c478bdstevel@tonic-gate	if (t_rcvdis(fd, &discon) == -1) {
12747c478bdstevel@tonic-gate		nfslib_log_tli_error("t_rcvdis", fd, nconf);
12757c478bdstevel@tonic-gate		return (-1);
12767c478bdstevel@tonic-gate	}
12777c478bdstevel@tonic-gate
12787c478bdstevel@tonic-gate	conn = *connp;
12797c478bdstevel@tonic-gate	if (conn == NULL)
12807c478bdstevel@tonic-gate		return (0);
12817c478bdstevel@tonic-gate
12827c478bdstevel@tonic-gate	do {
12837c478bdstevel@tonic-gate		if (conn->conn_call->sequence == discon.sequence) {
12847c478bdstevel@tonic-gate			if (conn->conn_next == conn)
12857c478bdstevel@tonic-gate				*connp = (struct conn_ind *)0;
12867c478bdstevel@tonic-gate			else {
12877c478bdstevel@tonic-gate				if (conn == *connp) {
12887c478bdstevel@tonic-gate					*connp = conn->conn_next;
12897c478bdstevel@tonic-gate				}
12907c478bdstevel@tonic-gate				conn->conn_next->conn_prev = conn->conn_prev;
12917c478bdstevel@tonic-gate				conn->conn_prev->conn_next = conn->conn_next;
12927c478bdstevel@tonic-gate			}
12937c478bdstevel@tonic-gate			free((char *)conn);
12947c478bdstevel@tonic-gate			break;
12957c478bdstevel@tonic-gate		}
12967c478bdstevel@tonic-gate		conn = conn->conn_next;
12977c478bdstevel@tonic-gate	} while (conn != *connp);
12987c478bdstevel@tonic-gate
12997c478bdstevel@tonic-gate	return (0);
13007c478bdstevel@tonic-gate}
13017c478bdstevel@tonic-gate
13027c478bdstevel@tonic-gatestatic void
13037c478bdstevel@tonic-gatecots_listen_event(int fd, int conn_index)
13047c478bdstevel@tonic-gate{
13057c478bdstevel@tonic-gate	struct t_call *call;
13067c478bdstevel@tonic-gate	struct conn_ind	*conn;
13077c478bdstevel@tonic-gate	struct conn_ind	*conn_head;
13087c478bdstevel@tonic-gate	int event;
13097c478bdstevel@tonic-gate	struct netconfig *nconf = &conn_polled[conn_index].nc;
13107c478bdstevel@tonic-gate	int new_fd;
13117c478bdstevel@tonic-gate	struct netbuf addrmask;
13127c478bdstevel@tonic-gate	int ret = 0;
13137c478bdstevel@tonic-gate	char *clnt;
13147c478bdstevel@tonic-gate	char *clnt_uaddr = NULL;
13157c478bdstevel@tonic-gate	struct nd_hostservlist *clnt_serv = NULL;
13167c478bdstevel@tonic-gate
13175539384Marcel Telka	conn_head = NULL;
13187c478bdstevel@tonic-gate	(void) conn_get(fd, nconf, &conn_head);
13197c478bdstevel@tonic-gate
13207c478bdstevel@tonic-gate	while ((conn = conn_head) != NULL) {
13217c478bdstevel@tonic-gate		conn_head = conn->conn_next;
13227c478bdstevel@tonic-gate		if (conn_head == conn)
13235539384Marcel Telka			conn_head = NULL;
13247c478bdstevel@tonic-gate		else {
13257c478bdstevel@tonic-gate			conn_head->conn_prev = conn->conn_prev;
13267c478bdstevel@tonic-gate			conn->conn_prev->conn_next = conn_head;
13277c478bdstevel@tonic-gate		}
13287c478bdstevel@tonic-gate		call = conn->conn_call;
13295539384Marcel Telka		free(conn);
13307c478bdstevel@tonic-gate
13317c478bdstevel@tonic-gate		/*
13327c478bdstevel@tonic-gate		 * If we have already accepted the maximum number of
13337c478bdstevel@tonic-gate		 * connections allowed on the command line, then drop
13347c478bdstevel@tonic-gate		 * the oldest connection (for any protocol) before
13357c478bdstevel@tonic-gate		 * accepting the new connection.  Unless explicitly
13367c478bdstevel@tonic-gate		 * set on the command line, max_conns_allowed is -1.
13377c478bdstevel@tonic-gate		 */
13387c478bdstevel@tonic-gate		if (max_conns_allowed != -1 && num_conns >= max_conns_allowed)
13397c478bdstevel@tonic-gate			conn_close_oldest();
13407c478bdstevel@tonic-gate
13417c478bdstevel@tonic-gate		/*
13427c478bdstevel@tonic-gate		 * Create a new transport endpoint for the same proto as
13437c478bdstevel@tonic-gate		 * the listener.
13447c478bdstevel@tonic-gate		 */
13457c478bdstevel@tonic-gate		new_fd = nfslib_transport_open(nconf);
13467c478bdstevel@tonic-gate		if (new_fd == -1) {
13477c478bdstevel@tonic-gate			call->udata.len = 0;
13487c478bdstevel@tonic-gate			(void) t_snddis(fd, call);
13497c478bdstevel@tonic-gate			(void) t_free((char *)call, T_CALL);
13507c478bdstevel@tonic-gate			syslog(LOG_ERR, "Cannot establish transport over %s",
1351e810a98vv			    nconf->nc_device);
13527c478bdstevel@tonic-gate			continue;
13537c478bdstevel@tonic-gate		}
13547c478bdstevel@tonic-gate
13557c478bdstevel@tonic-gate		/* Bind to a generic address/port for the accepting stream. */
13565539384Marcel Telka		if (t_bind(new_fd, NULL, NULL) == -1) {
13577c478bdstevel@tonic-gate			nfslib_log_tli_error("t_bind", new_fd, nconf);
13587c478bdstevel@tonic-gate			call->udata.len = 0;
13597c478bdstevel@tonic-gate			(void) t_snddis(fd, call);
13607c478bdstevel@tonic-gate			(void) t_free((char *)call, T_CALL);
13617c478bdstevel@tonic-gate			(void) t_close(new_fd);
13627c478bdstevel@tonic-gate			continue;
13637c478bdstevel@tonic-gate		}
13647c478bdstevel@tonic-gate
13657c478bdstevel@tonic-gate		while (t_accept(fd, new_fd, call) == -1) {
13667c478bdstevel@tonic-gate			if (t_errno != TLOOK) {
13677c478bdstevel@tonic-gate#ifdef DEBUG
13687c478bdstevel@tonic-gate				nfslib_log_tli_error("t_accept", fd, nconf);
13697c478bdstevel@tonic-gate#endif
13707c478bdstevel@tonic-gate				call->udata.len = 0;
13717c478bdstevel@tonic-gate				(void) t_snddis(fd, call);
13727c478bdstevel@tonic-gate				(void) t_free((char *)call, T_CALL);
13737c478bdstevel@tonic-gate				(void) t_close(new_fd);
13747c478bdstevel@tonic-gate				goto do_next_conn;
13757c478bdstevel@tonic-gate			}
13767c478bdstevel@tonic-gate			while (event = t_look(fd)) {
13777c478bdstevel@tonic-gate				switch (event) {
13787c478bdstevel@tonic-gate				case T_LISTEN:
13797c478bdstevel@tonic-gate#ifdef DEBUG
13807c478bdstevel@tonic-gate					printf(
13817c478bdstevel@tonic-gate"cots_listen_event(%s): T_LISTEN during accept processing\n", nconf->nc_proto);
13827c478bdstevel@tonic-gate#endif
13837c478bdstevel@tonic-gate					(void) conn_get(fd, nconf, &conn_head);
13847c478bdstevel@tonic-gate					continue;
13857c478bdstevel@tonic-gate				case T_DISCONNECT:
13867c478bdstevel@tonic-gate#ifdef DEBUG
13877c478bdstevel@tonic-gate					printf(
13887c478bdstevel@tonic-gate	"cots_listen_event(%s): T_DISCONNECT during accept processing\n",
1389e810a98vv					    nconf->nc_proto);
13907c478bdstevel@tonic-gate#endif
13917c478bdstevel@tonic-gate					(void) discon_get(fd, nconf,
1392e810a98vv					    &conn_head);
13937c478bdstevel@tonic-gate					continue;
13947c478bdstevel@tonic-gate				default:
13957c478bdstevel@tonic-gate					syslog(LOG_ERR,
13967c478bdstevel@tonic-gate			"unexpected event 0x%x during accept processing (%s)",
1397e810a98vv					    event, nconf->nc_proto);
13987c478bdstevel@tonic-gate					call->udata.len = 0;
13997c478bdstevel@tonic-gate					(void) t_snddis(fd, call);
14007c478bdstevel@tonic-gate					(void) t_free((char *)call, T_CALL);
14017c478bdstevel@tonic-gate					(void) t_close(new_fd);
14027c478bdstevel@tonic-gate					goto do_next_conn;
14037c478bdstevel@tonic-gate				}
14047c478bdstevel@tonic-gate			}
14057c478bdstevel@tonic-gate		}
14067c478bdstevel@tonic-gate
14077c478bdstevel@tonic-gate		if (set_addrmask(new_fd, nconf, &addrmask) < 0) {
14087c478bdstevel@tonic-gate			(void) syslog(LOG_ERR,
14097c478bdstevel@tonic-gate			    "Cannot set address mask for %s",
1410e810a98vv			    nconf->nc_netid);
14115539384Marcel Telka			(void) t_snddis(new_fd, NULL);
14125539384Marcel Telka			(void) t_free((char *)call, T_CALL);
14135539384Marcel Telka			(void) t_close(new_fd);
14145539384Marcel Telka			continue;
14157c478bdstevel@tonic-gate		}
14167c478bdstevel@tonic-gate
14175539384Marcel Telka		/* Tell kRPC about the new stream. */
14187c478bdstevel@tonic-gate		if (Mysvc4 != NULL)
14197c478bdstevel@tonic-gate			ret = (*Mysvc4)(new_fd, &addrmask, nconf,
1420e810a98vv			    NFS4_KRPC_START, &call->addr);
14217c478bdstevel@tonic-gate		else
14227c478bdstevel@tonic-gate			ret = (*Mysvc)(new_fd, addrmask, nconf);
14237c478bdstevel@tonic-gate
14247c478bdstevel@tonic-gate		if (ret < 0) {
14257c478bdstevel@tonic-gate			if (errno != ENOTCONN) {
14267c478bdstevel@tonic-gate				syslog(LOG_ERR,
14277c478bdstevel@tonic-gate				    "unable to register new connection: %m");
14287c478bdstevel@tonic-gate			} else {
14297c478bdstevel@tonic-gate				/*
14307c478bdstevel@tonic-gate				 * This is the only error that could be
14317c478bdstevel@tonic-gate				 * caused by the client, so who was it?
14327c478bdstevel@tonic-gate				 */
14337c478bdstevel@tonic-gate				if (netdir_getbyaddr(nconf, &clnt_serv,
14347c478bdstevel@tonic-gate				    &(call->addr)) == ND_OK &&
14357c478bdstevel@tonic-gate				    clnt_serv->h_cnt > 0)
14367c478bdstevel@tonic-gate					clnt = clnt_serv->h_hostservs->h_host;
14377c478bdstevel@tonic-gate				else
14387c478bdstevel@tonic-gate					clnt = clnt_uaddr = taddr2uaddr(nconf,
14397c478bdstevel@tonic-gate					    &(call->addr));
14407c478bdstevel@tonic-gate				/*
14417c478bdstevel@tonic-gate				 * If we don't know who the client was,
14427c478bdstevel@tonic-gate				 * remain silent.
14437c478bdstevel@tonic-gate				 */
14447c478bdstevel@tonic-gate				if (clnt)
14457c478bdstevel@tonic-gate					syslog(LOG_ERR,
14467c478bdstevel@tonic-gate"unable to register new connection: client %s has dropped connection", clnt);
14475539384Marcel Telka				if (clnt_serv) {
14487c478bdstevel@tonic-gate					netdir_free(clnt_serv, ND_HOSTSERVLIST);
14495539384Marcel Telka					clnt_serv = NULL;
14505539384Marcel Telka				}
14515539384Marcel Telka				if (clnt_uaddr) {
14527c478bdstevel@tonic-gate					free(clnt_uaddr);
14535539384Marcel Telka					clnt_uaddr = NULL;
14545539384Marcel Telka				}
14557c478bdstevel@tonic-gate			}
14567c478bdstevel@tonic-gate			free(addrmask.buf);
14575539384Marcel Telka			(void) t_snddis(new_fd, NULL);
14587c478bdstevel@tonic-gate			(void) t_free((char *)call, T_CALL);
14597c478bdstevel@tonic-gate			(void) t_close(new_fd);
14607c478bdstevel@tonic-gate			goto do_next_conn;
14617c478bdstevel@tonic-gate		}
14627c478bdstevel@tonic-gate
14637c478bdstevel@tonic-gate		free(addrmask.buf);
14647c478bdstevel@tonic-gate		(void) t_free((char *)call, T_CALL);
14657c478bdstevel@tonic-gate
14667c478bdstevel@tonic-gate		/*
14677c478bdstevel@tonic-gate		 * Poll on the new descriptor so that we get disconnect
14687c478bdstevel@tonic-gate		 * and orderly release indications.
14697c478bdstevel@tonic-gate		 */
14707c478bdstevel@tonic-gate		num_conns++;
14717c478bdstevel@tonic-gate		add_to_poll_list(new_fd, nconf);
14727c478bdstevel@tonic-gate
14737c478bdstevel@tonic-gate		/* Reset nconf in case it has been moved. */
14747c478bdstevel@tonic-gate		nconf = &conn_polled[conn_index].nc;
14757c478bdstevel@tonic-gatedo_next_conn:;
14767c478bdstevel@tonic-gate	}
14777c478bdstevel@tonic-gate}
14787c478bdstevel@tonic-gate
14797c478bdstevel@tonic-gatestatic int
14807c478bdstevel@tonic-gatedo_poll_cots_action(int fd, int conn_index)
14817c478bdstevel@tonic-gate{
14827c478bdstevel@tonic-gate	char buf[256];
14837c478bdstevel@tonic-gate	int event;
14847c478bdstevel@tonic-gate	int i1;
14857c478bdstevel@tonic-gate	int flags;
14867c478bdstevel@tonic-gate	struct conn_entry *connent = &conn_polled[conn_index];
14877c478bdstevel@tonic-gate	struct netconfig *nconf = &(connent->nc);
14887c478bdstevel@tonic-gate	const char *errorstr;
14897c478bdstevel@tonic-gate
14907c478bdstevel@tonic-gate	while (event = t_look(fd)) {
14917c478bdstevel@tonic-gate		switch (event) {
14927c478bdstevel@tonic-gate		case T_LISTEN:
14937c478bdstevel@tonic-gate#ifdef DEBUG
14947c478bdstevel@tonic-gateprintf("do_poll_cots_action(%s,%d): T_LISTEN event\n", nconf->nc_proto, fd);
14957c478bdstevel@tonic-gate#endif
14967c478bdstevel@tonic-gate			cots_listen_event(fd, conn_index);
14977c478bdstevel@tonic-gate			break;
14987c478bdstevel@tonic-gate
14997c478bdstevel@tonic-gate		case T_DATA:
15007c478bdstevel@tonic-gate#ifdef DEBUG
15017c478bdstevel@tonic-gateprintf("do_poll_cots_action(%d,%s): T_DATA event\n", fd, nconf->nc_proto);
15027c478bdstevel@tonic-gate#endif
15037c478bdstevel@tonic-gate			/*
15047c478bdstevel@tonic-gate			 * Receive a private notification from CONS rpcmod.
15057c478bdstevel@tonic-gate			 */
15067c478bdstevel@tonic-gate			i1 = t_rcv(fd, buf, sizeof (buf), &flags);
15077c478bdstevel@tonic-gate			if (i1 == -1) {
15087c478bdstevel@tonic-gate				syslog(LOG_ERR, "t_rcv failed");
15097c478bdstevel@tonic-gate				break;
15107c478bdstevel@tonic-gate			}
15117c478bdstevel@tonic-gate			if (i1 < sizeof (int))
15127c478bdstevel@tonic-gate				break;
15137c478bdstevel@tonic-gate			i1 = BE32_TO_U32(buf);
15147c478bdstevel@tonic-gate			if (i1 == 1 || i1 == 2) {
15157c478bdstevel@tonic-gate				/*
15167c478bdstevel@tonic-gate				 * This connection has been idle for too long,
15177c478bdstevel@tonic-gate				 * so release it as politely as we can.  If we
15187c478bdstevel@tonic-gate				 * have already initiated an orderly release
15197c478bdstevel@tonic-gate				 * and we get notified that the stream is
15207c478bdstevel@tonic-gate				 * still idle, pull the plug.  This prevents
15217c478bdstevel@tonic-gate				 * hung connections from continuing to consume
15227c478bdstevel@tonic-gate				 * resources.
15237c478bdstevel@tonic-gate				 */
15247c478bdstevel@tonic-gate#ifdef DEBUG
15257c478bdstevel@tonic-gateprintf("do_poll_cots_action(%s,%d): ", nconf->nc_proto, fd);
15267c478bdstevel@tonic-gateprintf("initiating orderly release of idle connection\n");
15277c478bdstevel@tonic-gate#endif
15287c478bdstevel@tonic-gate				if (nconf->nc_semantics == NC_TPI_COTS ||
1529e810a98vv				    connent->closing != 0) {
15307c478bdstevel@tonic-gate					(void) t_snddis(fd, (struct t_call *)0);
15317c478bdstevel@tonic-gate					goto fdclose;
15327c478bdstevel@tonic-gate				}
15337c478bdstevel@tonic-gate				/*
15347c478bdstevel@tonic-gate				 * For NC_TPI_COTS_ORD, the stream is closed
15357c478bdstevel@tonic-gate				 * and removed from the poll list when the
15367c478bdstevel@tonic-gate				 * T_ORDREL is received from the provider.  We
15377c478bdstevel@tonic-gate				 * don't wait for it here because it may take
15387c478bdstevel@tonic-gate				 * a while for the transport to shut down.
15397c478bdstevel@tonic-gate				 */
15407c478bdstevel@tonic-gate				if (t_sndrel(fd) == -1) {
15417c478bdstevel@tonic-gate					syslog(LOG_ERR,
15427c478bdstevel@tonic-gate					"unable to send orderly release %m");
15437c478bdstevel@tonic-gate				}
15447c478bdstevel@tonic-gate				connent->closing = 1;
15457c478bdstevel@tonic-gate			} else
15467c478bdstevel@tonic-gate				syslog(LOG_ERR,
15477c478bdstevel@tonic-gate				"unexpected event from CONS rpcmod %d", i1);
15487c478bdstevel@tonic-gate			break;
15497c478bdstevel@tonic-gate
15507c478bdstevel@tonic-gate		case T_ORDREL:
15517c478bdstevel@tonic-gate#ifdef DEBUG
15527c478bdstevel@tonic-gateprintf("do_poll_cots_action(%s,%d): T_ORDREL event\n", nconf->nc_proto, fd);
15537c478bdstevel@tonic-gate#endif
15547c478bdstevel@tonic-gate			/* Perform an orderly release. */
15557c478bdstevel@tonic-gate			if (t_rcvrel(fd) == 0) {
15567c478bdstevel@tonic-gate				/* T_ORDREL on listen fd's should be ignored */
15577c478bdstevel@tonic-gate				if (!is_listen_fd_index(conn_index)) {
15587c478bdstevel@tonic-gate					(void) t_sndrel(fd);
15597c478bdstevel@tonic-gate					goto fdclose;
15607c478bdstevel@tonic-gate				}
15617c478bdstevel@tonic-gate				break;
15627c478bdstevel@tonic-gate
15637c478bdstevel@tonic-gate			} else if (t_errno == TLOOK) {
15647c478bdstevel@tonic-gate				break;
15657c478bdstevel@tonic-gate			} else {
15667c478bdstevel@tonic-gate				nfslib_log_tli_error("t_rcvrel", fd, nconf);
15677c478bdstevel@tonic-gate
15687c478bdstevel@tonic-gate				/*
15697c478bdstevel@tonic-gate				 * check to make sure we do not close
15707c478bdstevel@tonic-gate				 * listen fd
15717c478bdstevel@tonic-gate				 */
15727c478bdstevel@tonic-gate				if (is_listen_fd_index(conn_index))
15737c478bdstevel@tonic-gate					break;
15747c478bdstevel@tonic-gate				else
15757c478bdstevel@tonic-gate					goto fdclose;
15767c478bdstevel@tonic-gate			}
15777c478bdstevel@tonic-gate
15787c478bdstevel@tonic-gate		case T_DISCONNECT:
15797c478bdstevel@tonic-gate#ifdef DEBUG
15807c478bdstevel@tonic-gateprintf("do_poll_cots_action(%s,%d): T_DISCONNECT event\n", nconf->nc_proto, fd);
15817c478bdstevel@tonic-gate#endif
15827c478bdstevel@tonic-gate			if (t_rcvdis(fd, (struct t_discon *)NULL) == -1)
15837c478bdstevel@tonic-gate				nfslib_log_tli_error("t_rcvdis", fd, nconf);
15847c478bdstevel@tonic-gate
15857c478bdstevel@tonic-gate			/*
15867c478bdstevel@tonic-gate			 * T_DISCONNECT on listen fd's should be ignored.
15877c478bdstevel@tonic-gate			 */
15887c478bdstevel@tonic-gate			if (is_listen_fd_index(conn_index))
15897c478bdstevel@tonic-gate				break;
15907c478bdstevel@tonic-gate			else
15917c478bdstevel@tonic-gate				goto fdclose;
15927c478bdstevel@tonic-gate
15937c478bdstevel@tonic-gate		default:
159409b0d01Gary Mills			if (t_errno == TSYSERR) {
1595e810a98vv				if ((errorstr = strerror(errno)) == NULL) {
1596e810a98vv					(void) sprintf(buf,
1597e810a98vv					    "Unknown error num %d", errno);
1598e810a98vv					errorstr = (const char *) buf;
1599e810a98vv				}
16007c478bdstevel@tonic-gate			} else if (event == -1)
16017c478bdstevel@tonic-gate				errorstr = t_strerror(t_errno);
16027c478bdstevel@tonic-gate			else
16037c478bdstevel@tonic-gate				errorstr = "";
16047c478bdstevel@tonic-gate			syslog(LOG_ERR,
16057c478bdstevel@tonic-gate			    "unexpected TLI event (0x%x) on "
16067c478bdstevel@tonic-gate			    "connection-oriented transport(%s,%d):%s",
16077c478bdstevel@tonic-gate			    event, nconf->nc_proto, fd, errorstr);
16087c478bdstevel@tonic-gatefdclose:
16097c478bdstevel@tonic-gate			num_conns--;
16107c478bdstevel@tonic-gate			remove_from_poll_list(fd);
16117c478bdstevel@tonic-gate			(void) t_close(fd);
16127c478bdstevel@tonic-gate			return (0);
16137c478bdstevel@tonic-gate		}
16147c478bdstevel@tonic-gate	}
16157c478bdstevel@tonic-gate
16167c478bdstevel@tonic-gate	return (0);
16177c478bdstevel@tonic-gate}
16187c478bdstevel@tonic-gate
16197c478bdstevel@tonic-gatestatic char *
16207c478bdstevel@tonic-gateserv_name_to_port_name(char *name)
16217c478bdstevel@tonic-gate{
16227c478bdstevel@tonic-gate	/*
16237c478bdstevel@tonic-gate	 * Map service names (used primarily in logging) to
16247c478bdstevel@tonic-gate	 * RPC port names (used by netdir_*() routines).
16257c478bdstevel@tonic-gate	 */
16267c478bdstevel@tonic-gate	if (strcmp(name, "NFS") == 0) {
16277c478bdstevel@tonic-gate		return ("nfs");
16287c478bdstevel@tonic-gate	} else if (strcmp(name, "NLM") == 0) {
16297c478bdstevel@tonic-gate		return ("lockd");
16307c478bdstevel@tonic-gate	} else if (strcmp(name, "NFS4_CALLBACK") == 0) {
16317c478bdstevel@tonic-gate		return ("nfs4_callback");
16327c478bdstevel@tonic-gate	}
16337c478bdstevel@tonic-gate
16347c478bdstevel@tonic-gate	return ("unrecognized");
16357c478bdstevel@tonic-gate}
16367c478bdstevel@tonic-gate
16377c478bdstevel@tonic-gatestatic int
16387c478bdstevel@tonic-gatebind_to_provider(char *provider, char *serv, struct netbuf **addr,
16397c478bdstevel@tonic-gate		struct netconfig **retnconf)
16407c478bdstevel@tonic-gate{
16417c478bdstevel@tonic-gate	struct netconfig *nconf;
16427c478bdstevel@tonic-gate	NCONF_HANDLE *nc;
16437c478bdstevel@tonic-gate	struct nd_hostserv hs;
16447c478bdstevel@tonic-gate
16457c478bdstevel@tonic-gate	hs.h_host = HOST_SELF;
16467c478bdstevel@tonic-gate	hs.h_serv = serv_name_to_port_name(serv);
16477c478bdstevel@tonic-gate
16487c478bdstevel@tonic-gate	if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
16497c478bdstevel@tonic-gate		syslog(LOG_ERR, "setnetconfig failed: %m");
16507c478bdstevel@tonic-gate		return (-1);
16517c478bdstevel@tonic-gate	}
16527c478bdstevel@tonic-gate	while (nconf = getnetconfig(nc)) {
16537c478bdstevel@tonic-gate		if (OK_TPI_TYPE(nconf) &&
16547c478bdstevel@tonic-gate		    strcmp(nconf->nc_device, provider) == 0) {
16557c478bdstevel@tonic-gate			*retnconf = nconf;
16567c478bdstevel@tonic-gate			return (nfslib_bindit(nconf, addr, &hs,
1657e810a98vv			    listen_backlog));
16587c478bdstevel@tonic-gate		}
16597c478bdstevel@tonic-gate	}
16607c478bdstevel@tonic-gate	(void) endnetconfig(nc);
16617c478bdstevel@tonic-gate
16627c478bdstevel@tonic-gate	syslog(LOG_ERR, "couldn't find netconfig entry for provider %s",
16637c478bdstevel@tonic-gate	    provider);
16647c478bdstevel@tonic-gate	return (-1);
16657c478bdstevel@tonic-gate}
16667c478bdstevel@tonic-gate
16677c478bdstevel@tonic-gatestatic int
16687c478bdstevel@tonic-gatebind_to_proto(NETSELDECL(proto), char *serv, struct netbuf **addr,
16697c478bdstevel@tonic-gate		struct netconfig **retnconf)
16707c478bdstevel@tonic-gate{
16717c478bdstevel@tonic-gate	struct netconfig *nconf;
16727c478bdstevel@tonic-gate	NCONF_HANDLE *nc = NULL;
16737c478bdstevel@tonic-gate	struct nd_hostserv hs;
16747c478bdstevel@tonic-gate
16757c478bdstevel@tonic-gate	hs.h_host = HOST_SELF;
16767c478bdstevel@tonic-gate	hs.h_serv = serv_name_to_port_name(serv);
16777c478bdstevel@tonic-gate
16787c478bdstevel@tonic-gate	if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
16797c478bdstevel@tonic-gate		syslog(LOG_ERR, "setnetconfig failed: %m");
16807c478bdstevel@tonic-gate		return (-1);
16817c478bdstevel@tonic-gate	}
16827c478bdstevel@tonic-gate	while (nconf = getnetconfig(nc)) {
16837c478bdstevel@tonic-gate		if (OK_TPI_TYPE(nconf) && NETSELEQ(nconf->nc_proto, proto)) {
16847c478bdstevel@tonic-gate			*retnconf = nconf;
16857c478bdstevel@tonic-gate			return (nfslib_bindit(nconf, addr, &hs,
1686e810a98vv			    listen_backlog));
16877c478bdstevel@tonic-gate		}
16887c478bdstevel@tonic-gate	}
16897c478bdstevel@tonic-gate	(void) endnetconfig(nc);
16907c478bdstevel@tonic-gate
16917c478bdstevel@tonic-gate	syslog(LOG_ERR, "couldn't find netconfig entry for protocol %s",
16927c478bdstevel@tonic-gate	    proto);
16937c478bdstevel@tonic-gate	return (-1);
16947c478bdstevel@tonic-gate}
16957c478bdstevel@tonic-gate
16967c478bdstevel@tonic-gate#include <netinet/in.h>
16977c478bdstevel@tonic-gate
16987c478bdstevel@tonic-gate/*
16997c478bdstevel@tonic-gate * Create an address mask appropriate for the transport.
17007c478bdstevel@tonic-gate * The mask is used to obtain the host-specific part of
17017c478bdstevel@tonic-gate * a network address when comparing addresses.
17027c478bdstevel@tonic-gate * For an internet address the host-specific part is just
17037c478bdstevel@tonic-gate * the 32 bit IP address and this part of the mask is set
17047c478bdstevel@tonic-gate * to all-ones. The port number part of the mask is zeroes.
17057c478bdstevel@tonic-gate */
17067c478bdstevel@tonic-gatestatic int
1707bbaa8b6Dan Kruchininset_addrmask(int fd,
1708bbaa8b6Dan Kruchinin	struct netconfig *nconf,
1709bbaa8b6Dan Kruchinin	struct netbuf *mask)
17107c478bdstevel@tonic-gate{
17117c478bdstevel@tonic-gate	struct t_info info;
17127c478bdstevel@tonic-gate
17137c478bdstevel@tonic-gate	/*
17147c478bdstevel@tonic-gate	 * Find the size of the address we need to mask.
17157c478bdstevel@tonic-gate	 */
17167c478bdstevel@tonic-gate	if (t_getinfo(fd, &info) < 0) {
17177c478bdstevel@tonic-gate		t_error("t_getinfo");
17187c478bdstevel@tonic-gate		return (-1);
17197c478bdstevel@tonic-gate	}
17207c478bdstevel@tonic-gate	mask->len = mask->maxlen = info.addr;
17217c478bdstevel@tonic-gate	if (info.addr <= 0) {
17224a03041Dan Kruchinin		/*
17234a03041Dan Kruchinin		 * loopback devices have infinite addr size
17244a03041Dan Kruchinin		 * (it is identified by -1 in addr field of t_info structure),
17254a03041Dan Kruchinin		 * so don't build the netmask for them. It's a special case
17264a03041Dan Kruchinin		 * that should be handled properly.
17274a03041Dan Kruchinin		 */
17284a03041Dan Kruchinin		if ((info.addr == -1) &&
17294a03041Dan Kruchinin		    (0 == strcmp(nconf->nc_protofmly, NC_LOOPBACK))) {
17304a03041Dan Kruchinin			memset(mask, 0, sizeof (*mask));
17314a03041Dan Kruchinin			return (0);
17324a03041Dan Kruchinin		}
17334a03041Dan Kruchinin
1734bbaa8b6Dan Kruchinin		syslog(LOG_ERR, "set_addrmask: address size: %ld", info.addr);
17357c478bdstevel@tonic-gate		return (-1);
17367c478bdstevel@tonic-gate	}
17377c478bdstevel@tonic-gate
17387c478bdstevel@tonic-gate	mask->buf = (char *)malloc(mask->len);
17397c478bdstevel@tonic-gate	if (mask->buf == NULL) {
17407c478bdstevel@tonic-gate		syslog(LOG_ERR, "set_addrmask: no memory");
17417c478bdstevel@tonic-gate		return (-1);
17427c478bdstevel@tonic-gate	}
17437c478bdstevel@tonic-gate	(void) memset(mask->buf, 0, mask->len);	/* reset all mask bits */
17447c478bdstevel@tonic-gate
17457c478bdstevel@tonic-gate	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
17467c478bdstevel@tonic-gate		/*
17477c478bdstevel@tonic-gate		 * Set the mask so that the port is ignored.
17487c478bdstevel@tonic-gate		 */
17497c478bdstevel@tonic-gate		/* LINTED pointer alignment */
17507c478bdstevel@tonic-gate		((struct sockaddr_in *)mask->buf)->sin_addr.s_addr =
1751bbaa8b6Dan Kruchinin		    (ulong_t)~0;
17527c478bdstevel@tonic-gate		/* LINTED pointer alignment */
17537c478bdstevel@tonic-gate		((struct sockaddr_in *)mask->buf)->sin_family =
1754bbaa8b6Dan Kruchinin		    (ushort_t)~0;
17557c478bdstevel@tonic-gate	} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
17567c478bdstevel@tonic-gate		/* LINTED pointer alignment */
17577c478bdstevel@tonic-gate		(void) memset(&((struct sockaddr_in6 *)mask->buf)->sin6_addr,
1758bbaa8b6Dan Kruchinin		    (uchar_t)~0, sizeof (struct in6_addr));
17597c478bdstevel@tonic-gate		/* LINTED pointer alignment */
17607c478bdstevel@tonic-gate		((struct sockaddr_in6 *)mask->buf)->sin6_family =
1761bbaa8b6Dan Kruchinin		    (ushort_t)~0;
17627c478bdstevel@tonic-gate	} else {
17637c478bdstevel@tonic-gate
17647c478bdstevel@tonic-gate		/*
17657c478bdstevel@tonic-gate		 * Set all mask bits.
17667c478bdstevel@tonic-gate		 */
17677c478bdstevel@tonic-gate		(void) memset(mask->buf, 0xFF, mask->len);
17687c478bdstevel@tonic-gate	}
17697c478bdstevel@tonic-gate	return (0);
17707c478bdstevel@tonic-gate}
17717c478bdstevel@tonic-gate
17727c478bdstevel@tonic-gate/*
17737c478bdstevel@tonic-gate * For listen fd's index is always less than end_listen_fds.
17747c478bdstevel@tonic-gate * end_listen_fds is defined externally in the daemon that uses this library.
17757c478bdstevel@tonic-gate * It's value is equal to the number of open file descriptors after the
17767c478bdstevel@tonic-gate * last listen end point was opened but before any connection was accepted.
17777c478bdstevel@tonic-gate */
17787c478bdstevel@tonic-gatestatic int
17797c478bdstevel@tonic-gateis_listen_fd_index(int index)
17807c478bdstevel@tonic-gate{
17817c478bdstevel@tonic-gate	return (index < end_listen_fds);
17827c478bdstevel@tonic-gate}
1783