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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
22*132Srobinson 
230Sstevel@tonic-gate /*
24*132Srobinson  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  *
270Sstevel@tonic-gate  * lib/libnsl/nss/netdir_inet_sundry.c
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * This file contains inet-specific implementations of netdir_options,
300Sstevel@tonic-gate  * uaddr2taddr, and taddr2uaddr. These implementations
310Sstevel@tonic-gate  * used to be in both tcpip.so and switch.so (identical copies).
320Sstevel@tonic-gate  * Since we got rid of those, and also it's a good idea to build-in
330Sstevel@tonic-gate  * inet-specific implementations in one place, we decided to put
340Sstevel@tonic-gate  * them in this file with a not-so glorious name. These are INET-SPECIFIC
350Sstevel@tonic-gate  * only, and will not be used for non-inet transports or by third-parties
360Sstevel@tonic-gate  * that decide to provide their own nametoaddr libs for inet transports
370Sstevel@tonic-gate  * (they are on their own for these as well => they get flexibility).
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  * Copied mostly from erstwhile lib/nametoaddr/tcpip/tcpip.c.
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #include "mt.h"
450Sstevel@tonic-gate #include <stdlib.h>
460Sstevel@tonic-gate #include <stdio.h>
470Sstevel@tonic-gate #include <string.h>
48*132Srobinson #include <strings.h>
490Sstevel@tonic-gate #include <unistd.h>
500Sstevel@tonic-gate #include <sys/types.h>
510Sstevel@tonic-gate #include <sys/stat.h>
520Sstevel@tonic-gate #include <fcntl.h>
530Sstevel@tonic-gate #include <errno.h>
540Sstevel@tonic-gate #include <thread.h>
550Sstevel@tonic-gate #include <netconfig.h>
560Sstevel@tonic-gate #include <netdir.h>
570Sstevel@tonic-gate #include <nss_netdir.h>
580Sstevel@tonic-gate #include <tiuser.h>
590Sstevel@tonic-gate #include <sys/socket.h>
600Sstevel@tonic-gate #include <net/if.h>
610Sstevel@tonic-gate #include <sys/sockio.h>
620Sstevel@tonic-gate #include <sys/fcntl.h>
630Sstevel@tonic-gate #include <netinet/in.h>
640Sstevel@tonic-gate #include <netinet/tcp.h>
650Sstevel@tonic-gate #include <netinet/udp.h>
660Sstevel@tonic-gate #include <arpa/inet.h>
670Sstevel@tonic-gate #include <rpc/types.h>
680Sstevel@tonic-gate #include <rpc/rpc_com.h>
690Sstevel@tonic-gate #include <syslog.h>
700Sstevel@tonic-gate #include <values.h>
710Sstevel@tonic-gate #include <limits.h>
720Sstevel@tonic-gate #ifdef DEBUG
730Sstevel@tonic-gate #include <stdio.h>
740Sstevel@tonic-gate #endif
750Sstevel@tonic-gate #include <nss_dbdefs.h>
760Sstevel@tonic-gate #include "nss.h"
770Sstevel@tonic-gate 
780Sstevel@tonic-gate #define	MAXIFS 32
790Sstevel@tonic-gate 
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate  * Extracted from socketvar.h
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate #define	SOV_DEFAULT	1	/* Select based on so_default_version */
840Sstevel@tonic-gate #define	SOV_SOCKBSD	3	/* Socket with no streams operations */
850Sstevel@tonic-gate 
86*132Srobinson extern int _so_socket(int, int, int, char *, int);
87*132Srobinson extern int _so_connect(int, struct sockaddr *, socklen_t, int);
88*132Srobinson extern int _so_getsockname(int, struct sockaddr *, socklen_t *, int);
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 
910Sstevel@tonic-gate static char *inet_netdir_mergeaddr(struct netconfig *, char *, char *);
920Sstevel@tonic-gate static int bindresvport(struct netconfig *, int, struct netbuf *);
930Sstevel@tonic-gate static int checkresvport(struct netbuf *);
940Sstevel@tonic-gate static struct netbuf *ip_uaddr2taddr(char *);
950Sstevel@tonic-gate static struct netbuf *ipv6_uaddr2taddr(char *);
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 
980Sstevel@tonic-gate extern char *inet_ntoa_r(struct in_addr, char *);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate int
101*132Srobinson __inet_netdir_options(struct netconfig *tp, int opts, int fd, char *par)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	struct nd_mergearg *ma;
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	switch (opts) {
1060Sstevel@tonic-gate 	case ND_SET_BROADCAST:
1070Sstevel@tonic-gate 		/* Every one is allowed to broadcast without asking */
1080Sstevel@tonic-gate 		return (ND_OK);
1090Sstevel@tonic-gate 	case ND_SET_RESERVEDPORT:	/* bind to a resered port */
110*132Srobinson 		/* LINTED pointer cast */
1110Sstevel@tonic-gate 		return (bindresvport(tp, fd, (struct netbuf *)par));
1120Sstevel@tonic-gate 	case ND_CHECK_RESERVEDPORT:	/* check if reserved prot */
113*132Srobinson 		/* LINTED pointer cast */
1140Sstevel@tonic-gate 		return (checkresvport((struct netbuf *)par));
1150Sstevel@tonic-gate 	case ND_MERGEADDR:	/* Merge two addresses */
116*132Srobinson 		/* LINTED pointer cast */
1170Sstevel@tonic-gate 		ma = (struct nd_mergearg *)(par);
1180Sstevel@tonic-gate 		ma->m_uaddr = inet_netdir_mergeaddr(tp, ma->c_uaddr,
1190Sstevel@tonic-gate 		    ma->s_uaddr);
1200Sstevel@tonic-gate 		return (_nderror);
1210Sstevel@tonic-gate 	default:
1220Sstevel@tonic-gate 		return (ND_NOCTRL);
1230Sstevel@tonic-gate 	}
1240Sstevel@tonic-gate }
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate /*
1280Sstevel@tonic-gate  * This routine will convert a TCP/IP internal format address
1290Sstevel@tonic-gate  * into a "universal" format address. In our case it prints out the
1300Sstevel@tonic-gate  * decimal dot equivalent. h1.h2.h3.h4.p1.p2 where h1-h4 are the host
1310Sstevel@tonic-gate  * address and p1-p2 are the port number.
1320Sstevel@tonic-gate  */
1330Sstevel@tonic-gate char *
134*132Srobinson __inet_taddr2uaddr(struct netconfig *tp, struct netbuf *addr)
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate 	struct sockaddr_in	*sa;	/* our internal format */
1370Sstevel@tonic-gate 	struct sockaddr_in6	*sa6;	/* our internal format */
1380Sstevel@tonic-gate 	char			tmp[RPC_INET6_MAXUADDRSIZE];
1390Sstevel@tonic-gate 	unsigned short		myport;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	if (addr == NULL || tp == NULL || addr->buf == NULL) {
1420Sstevel@tonic-gate 		_nderror = ND_BADARG;
1430Sstevel@tonic-gate 		return (NULL);
1440Sstevel@tonic-gate 	}
1450Sstevel@tonic-gate 	if (strcmp(tp->nc_protofmly, NC_INET) == 0) {
146*132Srobinson 		/* LINTED pointer cast */
1470Sstevel@tonic-gate 		sa = (struct sockaddr_in *)(addr->buf);
1480Sstevel@tonic-gate 		myport = ntohs(sa->sin_port);
149*132Srobinson 		(void) inet_ntoa_r(sa->sin_addr, tmp);
1500Sstevel@tonic-gate 	} else {
151*132Srobinson 		/* LINTED pointer cast */
1520Sstevel@tonic-gate 		sa6 = (struct sockaddr_in6 *)(addr->buf);
1530Sstevel@tonic-gate 		myport = ntohs(sa6->sin6_port);
1540Sstevel@tonic-gate 		if (inet_ntop(AF_INET6, (void *)sa6->sin6_addr.s6_addr,
1550Sstevel@tonic-gate 			tmp, sizeof (tmp)) == 0) {
1560Sstevel@tonic-gate 			_nderror = ND_BADARG;
1570Sstevel@tonic-gate 			return (NULL);
1580Sstevel@tonic-gate 		}
1590Sstevel@tonic-gate 	}
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	(void) sprintf(tmp + strlen(tmp), ".%d.%d", myport >> 8, myport & 255);
1620Sstevel@tonic-gate 	return (strdup(tmp));	/* Doesn't return static data ! */
1630Sstevel@tonic-gate }
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate /*
1660Sstevel@tonic-gate  * This internal routine will convert one of those "universal" addresses
1670Sstevel@tonic-gate  * to the internal format used by the Sun TLI TCP/IP provider.
1680Sstevel@tonic-gate  */
1690Sstevel@tonic-gate struct netbuf *
170*132Srobinson __inet_uaddr2taddr(struct netconfig *tp, char *addr)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate 	if (!addr || !tp) {
1730Sstevel@tonic-gate 		_nderror = ND_BADARG;
1740Sstevel@tonic-gate 		return (NULL);
1750Sstevel@tonic-gate 	}
1760Sstevel@tonic-gate 	if (strcmp(tp->nc_protofmly, NC_INET) == 0)
1770Sstevel@tonic-gate 		return (ip_uaddr2taddr(addr));
1780Sstevel@tonic-gate 	else
1790Sstevel@tonic-gate 		return (ipv6_uaddr2taddr(addr));
1800Sstevel@tonic-gate }
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate static struct netbuf *
1830Sstevel@tonic-gate ip_uaddr2taddr(char *addr)
1840Sstevel@tonic-gate {
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 	struct sockaddr_in	*sa;
1870Sstevel@tonic-gate 	uint32_t		inaddr;
1880Sstevel@tonic-gate 	unsigned short		inport;
1890Sstevel@tonic-gate 	int			h1, h2, h3, h4, p1, p2;
1900Sstevel@tonic-gate 	struct netbuf		*result;
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	result = malloc(sizeof (struct netbuf));
1930Sstevel@tonic-gate 	if (!result) {
1940Sstevel@tonic-gate 		_nderror = ND_NOMEM;
1950Sstevel@tonic-gate 		return (NULL);
1960Sstevel@tonic-gate 	}
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	sa = calloc(1, sizeof (*sa));
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	if (!sa) {
2010Sstevel@tonic-gate 		free(result);
2020Sstevel@tonic-gate 		_nderror = ND_NOMEM;
2030Sstevel@tonic-gate 		return (NULL);
2040Sstevel@tonic-gate 	}
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	result->buf = (char *)(sa);
2070Sstevel@tonic-gate 	result->maxlen = sizeof (struct sockaddr_in);
2080Sstevel@tonic-gate 	result->len = sizeof (struct sockaddr_in);
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	/* XXX there is probably a better way to do this. */
2110Sstevel@tonic-gate 	if (sscanf(addr, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4,
2120Sstevel@tonic-gate 	    &p1, &p2) != 6) {
2130Sstevel@tonic-gate 		free(result);
2140Sstevel@tonic-gate 		_nderror = ND_NO_RECOVERY;
2150Sstevel@tonic-gate 		return (NULL);
2160Sstevel@tonic-gate 	}
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	/* convert the host address first */
2190Sstevel@tonic-gate 	inaddr = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;
2200Sstevel@tonic-gate 	sa->sin_addr.s_addr = htonl(inaddr);
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	/* convert the port */
2230Sstevel@tonic-gate 	inport = (p1 << 8) + p2;
2240Sstevel@tonic-gate 	sa->sin_port = htons(inport);
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	sa->sin_family = AF_INET;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	return (result);
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate static struct netbuf *
2320Sstevel@tonic-gate ipv6_uaddr2taddr(char	*addr)
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate 	struct sockaddr_in6	*sa;
2350Sstevel@tonic-gate 	unsigned short		inport;
2360Sstevel@tonic-gate 	int	 p1, p2;
2370Sstevel@tonic-gate 	struct netbuf		*result;
2380Sstevel@tonic-gate 	char tmpaddr[RPC_INET6_MAXUADDRSIZE];
2390Sstevel@tonic-gate 	char *dot;
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	result = malloc(sizeof (struct netbuf));
2420Sstevel@tonic-gate 	if (!result) {
2430Sstevel@tonic-gate 		_nderror = ND_NOMEM;
2440Sstevel@tonic-gate 		return (NULL);
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	sa = calloc(1, sizeof (struct sockaddr_in6));
2480Sstevel@tonic-gate 	if (!sa) {
2490Sstevel@tonic-gate 		free(result);
2500Sstevel@tonic-gate 		_nderror = ND_NOMEM;
2510Sstevel@tonic-gate 		return (NULL);
2520Sstevel@tonic-gate 	}
2530Sstevel@tonic-gate 	result->buf = (char *)(sa);
2540Sstevel@tonic-gate 	result->maxlen = sizeof (struct sockaddr_in6);
2550Sstevel@tonic-gate 	result->len = sizeof (struct sockaddr_in6);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	/* retrieve the ipv6 address and port info */
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	if (strlen(addr) > sizeof (tmpaddr) - 1) {
2600Sstevel@tonic-gate 		free(result);
2610Sstevel@tonic-gate 		_nderror = ND_NOMEM;
2620Sstevel@tonic-gate 		return (NULL);
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 
265*132Srobinson 	(void) strcpy(tmpaddr, addr);
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	if ((dot = strrchr(tmpaddr, '.')) != 0) {
2680Sstevel@tonic-gate 		*dot = '\0';
2690Sstevel@tonic-gate 		p2 = atoi(dot+1);
2700Sstevel@tonic-gate 		if ((dot = strrchr(tmpaddr, '.')) != 0) {
2710Sstevel@tonic-gate 			*dot = '\0';
2720Sstevel@tonic-gate 			p1 = atoi(dot+1);
2730Sstevel@tonic-gate 		}
2740Sstevel@tonic-gate 	}
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	if (dot == 0) {
2770Sstevel@tonic-gate 		free(result);
2780Sstevel@tonic-gate 		_nderror = ND_NOMEM;
2790Sstevel@tonic-gate 		return (NULL);
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	if (inet_pton(AF_INET6, tmpaddr, sa->sin6_addr.s6_addr) == 0) {
2830Sstevel@tonic-gate 		free(result);
2840Sstevel@tonic-gate 		_nderror = ND_NOMEM;
2850Sstevel@tonic-gate 		return (NULL);
2860Sstevel@tonic-gate 	}
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	/* convert the port */
2890Sstevel@tonic-gate 	inport = (p1 << 8) + p2;
2900Sstevel@tonic-gate 	sa->sin6_port = htons(inport);
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	sa->sin6_family = AF_INET6;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	return (result);
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate /*
2980Sstevel@tonic-gate  * Interface caching routines.  The cache is refreshed every
2990Sstevel@tonic-gate  * IF_CACHE_REFRESH_TIME seconds.  A read-write lock is used to
3000Sstevel@tonic-gate  * protect the cache.
3010Sstevel@tonic-gate  */
3020Sstevel@tonic-gate #define	IF_CACHE_REFRESH_TIME 10
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate static int if_cache_refresh_time = IF_CACHE_REFRESH_TIME;
3050Sstevel@tonic-gate static rwlock_t iflock = DEFAULTRWLOCK;
3060Sstevel@tonic-gate static time_t last_updated = 0;		/* protected by iflock */
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate /*
3090Sstevel@tonic-gate  * Changing the data type of if_flags from uint_t to uint64_t to accomodate
3100Sstevel@tonic-gate  * extra flags. Refer <net/if.h> for the extra flags.
3110Sstevel@tonic-gate  */
3120Sstevel@tonic-gate typedef struct if_info_s {
3130Sstevel@tonic-gate 	struct in_addr if_netmask;	/* netmask in network order */
3140Sstevel@tonic-gate 	struct in_addr if_address;	/* address in network order */
3150Sstevel@tonic-gate 	uint64_t if_flags;		/* interface flags */
3160Sstevel@tonic-gate } if_info_t;
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate static if_info_t *if_info = NULL;	/* if cache, protected by iflock */
3190Sstevel@tonic-gate static int n_ifs = 0;			/* number of cached interfaces */
3200Sstevel@tonic-gate static int numifs_last = 0;		/* number of interfaces last seen */
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate /*
3230Sstevel@tonic-gate  * Builds the interface cache.  Write lock on iflock is needed
3240Sstevel@tonic-gate  * for calling this routine.  It sets _nderror for error returns.
3250Sstevel@tonic-gate  * Returns TRUE if successful, FALSE otherwise.
3260Sstevel@tonic-gate  * Changing the structures ifreq and ifconf to lifreq and lifconf to
3270Sstevel@tonic-gate  * have larger flag field. This is to accomodate the extra flags associated
3280Sstevel@tonic-gate  * with the interface. Also introducing lifn which will contain the number
3290Sstevel@tonic-gate  * of IPV4 interfaces present.
3300Sstevel@tonic-gate  */
3310Sstevel@tonic-gate static bool_t
332*132Srobinson get_if_info(void)
3330Sstevel@tonic-gate {
3340Sstevel@tonic-gate 	size_t		needed;
3350Sstevel@tonic-gate 	struct lifreq	*buf = NULL;
3360Sstevel@tonic-gate 	int		numifs;
3370Sstevel@tonic-gate 	struct lifconf  lifc;
3380Sstevel@tonic-gate 	struct lifreq   *lifr;
3390Sstevel@tonic-gate 	struct lifnum   lifn;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	lifn.lifn_family = AF_INET;
3420Sstevel@tonic-gate 	lifn.lifn_flags = 0;
3430Sstevel@tonic-gate getifnum:
3440Sstevel@tonic-gate 	if (nss_ioctl(AF_INET, SIOCGLIFNUM, &lifn) == -1) {
3450Sstevel@tonic-gate 		numifs = MAXIFS;
3460Sstevel@tonic-gate 	} else {
3470Sstevel@tonic-gate 		numifs = lifn.lifn_count;
3480Sstevel@tonic-gate 	}
3490Sstevel@tonic-gate 	/*
3500Sstevel@tonic-gate 	 * Add a small fudge factor in case interfaces are plumbed
3510Sstevel@tonic-gate 	 * between the SIOCGLIFNUM and SIOCGLIFCONF.
3520Sstevel@tonic-gate 	 */
3530Sstevel@tonic-gate 	needed = (numifs + 4) * sizeof (struct lifreq);
3540Sstevel@tonic-gate 	if (buf == NULL)
3550Sstevel@tonic-gate 		buf = malloc(needed);
3560Sstevel@tonic-gate 	else
3570Sstevel@tonic-gate 		buf = realloc(buf, needed);
3580Sstevel@tonic-gate 	if (buf == NULL) {
3590Sstevel@tonic-gate 		_nderror = ND_NOMEM;
3600Sstevel@tonic-gate 		return (FALSE);
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	lifc.lifc_family = AF_INET;
3640Sstevel@tonic-gate 	lifc.lifc_flags = 0;
3650Sstevel@tonic-gate 	lifc.lifc_len = needed;
3660Sstevel@tonic-gate 	lifc.lifc_buf = (char *)buf;
3670Sstevel@tonic-gate 	if (nss_ioctl(AF_INET, SIOCGLIFCONF, &lifc) == -1) {
3680Sstevel@tonic-gate 		/*
3690Sstevel@tonic-gate 		 * IP returns EINVAL if the buffer was too small to fit
3700Sstevel@tonic-gate 		 * all of the entries.  If that's the case, go back and
3710Sstevel@tonic-gate 		 * try again.
3720Sstevel@tonic-gate 		 */
3730Sstevel@tonic-gate 		if (errno == EINVAL)
3740Sstevel@tonic-gate 			goto getifnum;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		free(buf);
3770Sstevel@tonic-gate 		free(if_info);
3780Sstevel@tonic-gate 		if_info = NULL;
3790Sstevel@tonic-gate 		_nderror = ND_SYSTEM;
3800Sstevel@tonic-gate 		return (FALSE);
3810Sstevel@tonic-gate 	}
3820Sstevel@tonic-gate 	numifs = lifc.lifc_len / (int)sizeof (struct lifreq);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	if (if_info == NULL || numifs > numifs_last) {
3850Sstevel@tonic-gate 		if (if_info == NULL)
3860Sstevel@tonic-gate 			if_info = malloc(numifs * sizeof (if_info_t));
3870Sstevel@tonic-gate 		else
3880Sstevel@tonic-gate 			if_info = realloc(if_info, numifs * sizeof (if_info_t));
3890Sstevel@tonic-gate 		if (if_info == NULL) {
3900Sstevel@tonic-gate 			free(buf);
3910Sstevel@tonic-gate 			_nderror = ND_NOMEM;
3920Sstevel@tonic-gate 			return (FALSE);
3930Sstevel@tonic-gate 		}
3940Sstevel@tonic-gate 		numifs_last = numifs;
3950Sstevel@tonic-gate 	}
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	n_ifs = 0;
3980Sstevel@tonic-gate 	for (lifr = buf; lifr < (buf + numifs); lifr++) {
3990Sstevel@tonic-gate 		if (lifr->lifr_addr.ss_family != AF_INET)
4000Sstevel@tonic-gate 			continue;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 		if_info[n_ifs].if_address =
4030Sstevel@tonic-gate 			((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 		if (nss_ioctl(AF_INET, SIOCGLIFFLAGS, lifr) < 0)
4060Sstevel@tonic-gate 			continue;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 		if ((lifr->lifr_flags & IFF_UP) == 0)
4090Sstevel@tonic-gate 			continue;
4100Sstevel@tonic-gate 		if_info[n_ifs].if_flags = lifr->lifr_flags;
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 		if (nss_ioctl(AF_INET, SIOCGLIFNETMASK, lifr) < 0)
4130Sstevel@tonic-gate 			continue;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 		if_info[n_ifs].if_netmask =
4160Sstevel@tonic-gate 			((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
4170Sstevel@tonic-gate 		n_ifs++;
4180Sstevel@tonic-gate 	}
4190Sstevel@tonic-gate 	free(buf);
4200Sstevel@tonic-gate 	return (TRUE);
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate /*
4250Sstevel@tonic-gate  * Update the interface cache based on last update time.
4260Sstevel@tonic-gate  */
4270Sstevel@tonic-gate static bool_t
428*132Srobinson update_if_cache(void)
4290Sstevel@tonic-gate {
4300Sstevel@tonic-gate 	time_t	curtime;
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	(void) rw_wrlock(&iflock);
4330Sstevel@tonic-gate 	/*
4340Sstevel@tonic-gate 	 * Check if some other thread has beaten this one to it.
4350Sstevel@tonic-gate 	 */
4360Sstevel@tonic-gate 	(void) time(&curtime);
4370Sstevel@tonic-gate 	if ((curtime - last_updated) >= if_cache_refresh_time) {
4380Sstevel@tonic-gate 		if (!get_if_info()) {
4390Sstevel@tonic-gate 			(void) rw_unlock(&iflock);
4400Sstevel@tonic-gate 			return (FALSE);
4410Sstevel@tonic-gate 		}
4420Sstevel@tonic-gate 		(void) time(&last_updated);
4430Sstevel@tonic-gate 	}
4440Sstevel@tonic-gate 	(void) rw_unlock(&iflock);
4450Sstevel@tonic-gate 	return (TRUE);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate /*
4500Sstevel@tonic-gate  * Given an IP address, check if this matches any of the interface
4510Sstevel@tonic-gate  * addresses.  If an error occurs, return FALSE so that the caller
4520Sstevel@tonic-gate  * will not assume that this address belongs to this machine.
4530Sstevel@tonic-gate  */
4540Sstevel@tonic-gate static bool_t
455*132Srobinson is_my_address(struct in_addr addr)
4560Sstevel@tonic-gate {
4570Sstevel@tonic-gate 	time_t		curtime;
4580Sstevel@tonic-gate 	if_info_t	*ifn;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	(void) time(&curtime);
4610Sstevel@tonic-gate 	if ((curtime - last_updated) >= if_cache_refresh_time) {
4620Sstevel@tonic-gate 		/*
4630Sstevel@tonic-gate 		 * Cache needs to be refreshed.
4640Sstevel@tonic-gate 		 */
4650Sstevel@tonic-gate 		if (!update_if_cache())
4660Sstevel@tonic-gate 			return (FALSE);
4670Sstevel@tonic-gate 	}
4680Sstevel@tonic-gate 	(void) rw_rdlock(&iflock);
4690Sstevel@tonic-gate 	for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
4700Sstevel@tonic-gate 		if (addr.s_addr == ifn->if_address.s_addr) {
4710Sstevel@tonic-gate 			(void) rw_unlock(&iflock);
4720Sstevel@tonic-gate 			return (TRUE);
4730Sstevel@tonic-gate 		}
4740Sstevel@tonic-gate 	}
4750Sstevel@tonic-gate 	(void) rw_unlock(&iflock);
4760Sstevel@tonic-gate 	return (FALSE);
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate /*
4810Sstevel@tonic-gate  * Given a host name, check if it is this host.
4820Sstevel@tonic-gate  */
4830Sstevel@tonic-gate bool_t
484*132Srobinson __inet_netdir_is_my_host(const char *host)
4850Sstevel@tonic-gate {
4860Sstevel@tonic-gate 	int		error;
4870Sstevel@tonic-gate 	char		buf[NSS_BUFLEN_HOSTS];
4880Sstevel@tonic-gate 	struct hostent	res, *h;
4890Sstevel@tonic-gate 	char		**c;
4900Sstevel@tonic-gate 	struct in_addr	in;
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	h = gethostbyname_r(host, (void *)&res, buf, sizeof (buf), &error);
4930Sstevel@tonic-gate 	if (h == NULL)
4940Sstevel@tonic-gate 		return (FALSE);
4950Sstevel@tonic-gate 	if (h->h_addrtype != AF_INET)
4960Sstevel@tonic-gate 		return (FALSE);
4970Sstevel@tonic-gate 	for (c = h->h_addr_list; *c != NULL; c++) {
498*132Srobinson 		(void) memcpy(&in.s_addr, *c, sizeof (in.s_addr));
4990Sstevel@tonic-gate 		if (is_my_address(in))
5000Sstevel@tonic-gate 			return (TRUE);
5010Sstevel@tonic-gate 	}
5020Sstevel@tonic-gate 	return (FALSE);
5030Sstevel@tonic-gate }
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate /*
5070Sstevel@tonic-gate  * Given an IP address, find the interface address that has the best
5080Sstevel@tonic-gate  * prefix match.  Return the address in network order.
5090Sstevel@tonic-gate  */
5100Sstevel@tonic-gate static uint32_t
511*132Srobinson get_best_match(struct in_addr addr)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate 	if_info_t *bestmatch, *ifn;
5140Sstevel@tonic-gate 	int bestcount, count, limit;
5150Sstevel@tonic-gate 	uint32_t mask, netmask, clnt_addr, if_addr;
5160Sstevel@tonic-gate 	bool_t found, subnet_match;
5170Sstevel@tonic-gate 	int subnet_count;
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	bestmatch = NULL;				/* no match yet */
5200Sstevel@tonic-gate 	bestcount = BITSPERBYTE * sizeof (uint32_t);	/* worst match */
5210Sstevel@tonic-gate 	clnt_addr = ntohl(addr.s_addr);			/* host order */
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	subnet_match = FALSE;		/* subnet match not found yet */
5240Sstevel@tonic-gate 	subnet_count = bestcount;	/* worst subnet match */
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
5270Sstevel@tonic-gate 		netmask = ntohl(ifn->if_netmask.s_addr);  /* host order */
5280Sstevel@tonic-gate 		if_addr = ntohl(ifn->if_address.s_addr);  /* host order */
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 		/*
5310Sstevel@tonic-gate 		 * Checking if the interface selected is FAILED or DEPRECATED.
5320Sstevel@tonic-gate 		 * In case IFF_FAILED or IFF_DEPRECATED flag for the interface
5330Sstevel@tonic-gate 		 * is set, we move on to the next interface in the list.
5340Sstevel@tonic-gate 		 * Refer IPMP(IP Multi Pathing) for more details.
5350Sstevel@tonic-gate 		 */
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 		if ((ifn->if_flags & (IFF_FAILED | IFF_DEPRECATED)) != 0)
5380Sstevel@tonic-gate 			continue;
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		/*
5410Sstevel@tonic-gate 		 * set initial count to first bit set in netmask, with
5420Sstevel@tonic-gate 		 * zero being the number of the least significant bit.
5430Sstevel@tonic-gate 		 */
5440Sstevel@tonic-gate 		for (count = 0, mask = netmask; mask && ((mask & 1) == 0);
5450Sstevel@tonic-gate 						count++, mask >>= 1);
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 		/*
5480Sstevel@tonic-gate 		 * Set limit so that we don't try to match prefixes shorter
5490Sstevel@tonic-gate 		 * than the inherent netmask for the class (A, B, C, etc).
5500Sstevel@tonic-gate 		 */
5510Sstevel@tonic-gate 		if (IN_CLASSC(if_addr))
5520Sstevel@tonic-gate 			limit = IN_CLASSC_NSHIFT;
5530Sstevel@tonic-gate 		else if (IN_CLASSB(if_addr))
5540Sstevel@tonic-gate 			limit = IN_CLASSB_NSHIFT;
5550Sstevel@tonic-gate 		else if (IN_CLASSA(if_addr))
5560Sstevel@tonic-gate 			limit = IN_CLASSA_NSHIFT;
5570Sstevel@tonic-gate 		else
5580Sstevel@tonic-gate 			limit = 0;
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 		/*
5610Sstevel@tonic-gate 		 * We assume that the netmask consists of a contiguous
5620Sstevel@tonic-gate 		 * sequence of 1-bits starting with the most significant bit.
5630Sstevel@tonic-gate 		 * Prefix comparison starts at the subnet mask level.
5640Sstevel@tonic-gate 		 * The prefix mask used for comparison is progressively
5650Sstevel@tonic-gate 		 * reduced until it equals the inherent mask for the
5660Sstevel@tonic-gate 		 * interface address class.  The algorithm finds an
5670Sstevel@tonic-gate 		 * interface in the following order of preference:
5680Sstevel@tonic-gate 		 *
5690Sstevel@tonic-gate 		 * (1) the longest subnet match
5700Sstevel@tonic-gate 		 * (2) the best partial subnet match
5710Sstevel@tonic-gate 		 * (3) the first non-loopback && non-PPP interface
5720Sstevel@tonic-gate 		 * (4) the first non-loopback interface (PPP is OK)
5730Sstevel@tonic-gate 		 *
5740Sstevel@tonic-gate 		 * While checking for condition (3) and (4), we also look
5750Sstevel@tonic-gate 		 * if the interface we are returning is neither FAILED
5760Sstevel@tonic-gate 		 * nor DEPRECATED. In case there are no interface
5770Sstevel@tonic-gate 		 * available, which are neither FAILED nor DEPRECRATED,
5780Sstevel@tonic-gate 		 * we return 0.
5790Sstevel@tonic-gate 		 */
5800Sstevel@tonic-gate 		found = FALSE;
5810Sstevel@tonic-gate 		while (netmask && count < subnet_count) {
5820Sstevel@tonic-gate 			if ((netmask & clnt_addr) == (netmask & if_addr)) {
5830Sstevel@tonic-gate 				bestcount = count;
5840Sstevel@tonic-gate 				bestmatch = ifn;
5850Sstevel@tonic-gate 				found = TRUE;
5860Sstevel@tonic-gate 				break;
5870Sstevel@tonic-gate 			}
5880Sstevel@tonic-gate 			netmask <<= 1;
5890Sstevel@tonic-gate 			count++;
5900Sstevel@tonic-gate 			if (count >= bestcount || count > limit || subnet_match)
5910Sstevel@tonic-gate 				break;
5920Sstevel@tonic-gate 		}
5930Sstevel@tonic-gate 		/*
5940Sstevel@tonic-gate 		 * If a subnet level match occurred, note this for
5950Sstevel@tonic-gate 		 * comparison with future subnet matches.
5960Sstevel@tonic-gate 		 */
5970Sstevel@tonic-gate 		if (found && (netmask == ntohl(ifn->if_netmask.s_addr))) {
5980Sstevel@tonic-gate 			subnet_match = TRUE;
5990Sstevel@tonic-gate 			subnet_count = count;
6000Sstevel@tonic-gate 		}
6010Sstevel@tonic-gate 	}
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	/*
6040Sstevel@tonic-gate 	 * If we don't have a match, select the first interface that
6050Sstevel@tonic-gate 	 * is not a loopback interface (and preferably not a PPP interface)
6060Sstevel@tonic-gate 	 * as the best match.
6070Sstevel@tonic-gate 	 */
6080Sstevel@tonic-gate 	if (bestmatch == NULL) {
6090Sstevel@tonic-gate 		for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
6100Sstevel@tonic-gate 			if ((ifn->if_flags & (IFF_LOOPBACK |
6110Sstevel@tonic-gate 				IFF_FAILED | IFF_DEPRECATED)) == 0) {
6120Sstevel@tonic-gate 				bestmatch = ifn;
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 				/*
6150Sstevel@tonic-gate 				 * If this isn't a PPP interface, we're
6160Sstevel@tonic-gate 				 * done.  Otherwise, keep walking through
6170Sstevel@tonic-gate 				 * the list in case we have a non-loopback
6180Sstevel@tonic-gate 				 * iface that ISN'T a PPP further down our
6190Sstevel@tonic-gate 				 * list...
6200Sstevel@tonic-gate 				 */
6210Sstevel@tonic-gate 				if ((ifn->if_flags & IFF_POINTOPOINT) == 0) {
6220Sstevel@tonic-gate #ifdef DEBUG
6230Sstevel@tonic-gate 		(void) printf("found !loopback && !non-PPP interface: %s\n",
6240Sstevel@tonic-gate 				inet_ntoa(ifn->if_address));
6250Sstevel@tonic-gate #endif
6260Sstevel@tonic-gate 					break;
6270Sstevel@tonic-gate 				}
6280Sstevel@tonic-gate 			}
6290Sstevel@tonic-gate 		}
6300Sstevel@tonic-gate 	}
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	if (bestmatch != NULL)
6330Sstevel@tonic-gate 		return (bestmatch->if_address.s_addr);
6340Sstevel@tonic-gate 	else
6350Sstevel@tonic-gate 		return (0);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate static int
6390Sstevel@tonic-gate is_myself(struct sockaddr_in6 *sa6)
6400Sstevel@tonic-gate {
6410Sstevel@tonic-gate 	struct sioc_addrreq areq;
6420Sstevel@tonic-gate 	int s;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	if ((s = open("/dev/udp6", O_RDONLY)) < 0) {
6450Sstevel@tonic-gate 		syslog(LOG_ERR, "is_myself: can't open /dev/udp6: %m");
6460Sstevel@tonic-gate 		return (0);
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 
649*132Srobinson 	(void) memcpy(&areq.sa_addr, sa6, sizeof (struct sockaddr_storage));
6500Sstevel@tonic-gate 	areq.sa_res = -1;
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) {
6530Sstevel@tonic-gate 		syslog(LOG_ERR, "is_myself:SIOCTMYADDR failed: %m");
654*132Srobinson 		(void) close(s);
6550Sstevel@tonic-gate 		return (0);
6560Sstevel@tonic-gate 	}
6570Sstevel@tonic-gate 
658*132Srobinson 	(void) close(s);
6590Sstevel@tonic-gate 	return (areq.sa_res);
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate /*
6630Sstevel@tonic-gate  * For a given destination address, determine a source address to use.
6640Sstevel@tonic-gate  * Returns wildcard address if it cannot determine the source address.
6650Sstevel@tonic-gate  * copied from ping.c.
6660Sstevel@tonic-gate  */
6670Sstevel@tonic-gate union any_in_addr {
6680Sstevel@tonic-gate 	struct in6_addr addr6;
6690Sstevel@tonic-gate 	struct in_addr addr;
6700Sstevel@tonic-gate };
671*132Srobinson 
6720Sstevel@tonic-gate static bool_t
6730Sstevel@tonic-gate select_server_addr(union any_in_addr *dst_addr, int family,
6740Sstevel@tonic-gate     union any_in_addr *src_addr)
6750Sstevel@tonic-gate {
6760Sstevel@tonic-gate 	struct sockaddr *sock;
6770Sstevel@tonic-gate 	struct sockaddr_in *sin;
6780Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
6790Sstevel@tonic-gate 	int tmp_fd;
680*132Srobinson 	socklen_t sock_len;
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	sock = calloc(1, sizeof (struct sockaddr_in6));
6830Sstevel@tonic-gate 	if (sock == NULL) {
6840Sstevel@tonic-gate 		return (FALSE);
6850Sstevel@tonic-gate 	}
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	if (family == AF_INET) {
688*132Srobinson 		/* LINTED pointer cast */
6890Sstevel@tonic-gate 		sin = (struct sockaddr_in *)sock;
6900Sstevel@tonic-gate 		sin->sin_family = AF_INET;
6910Sstevel@tonic-gate 		sin->sin_port = 111;
6920Sstevel@tonic-gate 		sin->sin_addr = dst_addr->addr;
6930Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in);
6940Sstevel@tonic-gate 	} else {
695*132Srobinson 		/* LINTED pointer cast */
6960Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)sock;
6970Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
6980Sstevel@tonic-gate 		sin6->sin6_port = 111;
6990Sstevel@tonic-gate 		sin6->sin6_addr = dst_addr->addr6;
7000Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in6);
7010Sstevel@tonic-gate 	}
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	/* open a UDP socket */
7040Sstevel@tonic-gate 	if ((tmp_fd = _so_socket(family, SOCK_DGRAM, 0,
7050Sstevel@tonic-gate 		NULL, SOV_SOCKBSD)) < 0) {
7060Sstevel@tonic-gate 		syslog(LOG_ERR, "selsect_server_addr:connect failed\n");
7070Sstevel@tonic-gate 		return (FALSE);
7080Sstevel@tonic-gate 	}
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	/* connect it */
7110Sstevel@tonic-gate 	if (_so_connect(tmp_fd, sock, sock_len, SOV_SOCKBSD) < 0) {
7120Sstevel@tonic-gate 		/*
7130Sstevel@tonic-gate 		 * If there's no route to the destination, this connect() call
7140Sstevel@tonic-gate 		 * fails. We just return all-zero (wildcard) as the source
7150Sstevel@tonic-gate 		 * address, so that user can get to see "no route to dest"
7160Sstevel@tonic-gate 		 * message, as it'll try to send the probe packet out and will
7170Sstevel@tonic-gate 		 * receive ICMP unreachable.
7180Sstevel@tonic-gate 		 */
7190Sstevel@tonic-gate 		if (family == AF_INET)
7200Sstevel@tonic-gate 			src_addr->addr.s_addr = INADDR_ANY;
7210Sstevel@tonic-gate 		else
7220Sstevel@tonic-gate 			/*
7230Sstevel@tonic-gate 			 * Since in6addr_any is not in the scope
7240Sstevel@tonic-gate 			 * use the following hack
7250Sstevel@tonic-gate 			 */
726*132Srobinson 			(void) memset(src_addr->addr6.s6_addr,
7270Sstevel@tonic-gate 				0, sizeof (struct in6_addr));
7280Sstevel@tonic-gate 		(void) close(tmp_fd);
7290Sstevel@tonic-gate 		free(sock);
7300Sstevel@tonic-gate 		return (FALSE);
7310Sstevel@tonic-gate 	}
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 	/* get the local sock info */
7340Sstevel@tonic-gate 	if (_so_getsockname(tmp_fd, sock, &sock_len, SOV_DEFAULT) < 0) {
7350Sstevel@tonic-gate 		syslog(LOG_ERR, "selsect_server_addr:getsockname failed\n");
7360Sstevel@tonic-gate 		(void) close(tmp_fd);
7370Sstevel@tonic-gate 		free(sock);
7380Sstevel@tonic-gate 		return (FALSE);
7390Sstevel@tonic-gate 	}
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	if (family == AF_INET) {
742*132Srobinson 		/* LINTED pointer cast */
7430Sstevel@tonic-gate 		sin = (struct sockaddr_in *)sock;
7440Sstevel@tonic-gate 		src_addr->addr = sin->sin_addr;
7450Sstevel@tonic-gate 	} else {
746*132Srobinson 		/* LINTED pointer cast */
7470Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)sock;
7480Sstevel@tonic-gate 		src_addr->addr6 = sin6->sin6_addr;
7490Sstevel@tonic-gate 	}
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	(void) close(tmp_fd);
7520Sstevel@tonic-gate 	free(sock);
7530Sstevel@tonic-gate 	return (TRUE);
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate /*
7570Sstevel@tonic-gate  * This internal routine will merge one of those "universal" addresses
7580Sstevel@tonic-gate  * to the one which will make sense to the remote caller.
7590Sstevel@tonic-gate  */
7600Sstevel@tonic-gate static char *
761*132Srobinson inet_netdir_mergeaddr(struct netconfig *tp, char *ruaddr, char *uaddr)
7620Sstevel@tonic-gate {
7630Sstevel@tonic-gate 	char	tmp[SYS_NMLN], *cp;
7640Sstevel@tonic-gate 	int	j;
7650Sstevel@tonic-gate 	struct	in_addr clientaddr, bestmatch;
7660Sstevel@tonic-gate 	time_t	curtime;
7670Sstevel@tonic-gate 	int af;
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	if (!uaddr || !ruaddr || !tp) {
7700Sstevel@tonic-gate 		_nderror = ND_BADARG;
7710Sstevel@tonic-gate 		return (NULL);
7720Sstevel@tonic-gate 	}
7730Sstevel@tonic-gate 	(void) bzero(tmp, SYS_NMLN);
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 	if (strcmp(tp->nc_protofmly, NC_INET) == 0)
7760Sstevel@tonic-gate 		af = AF_INET;
7770Sstevel@tonic-gate 	else
7780Sstevel@tonic-gate 		af = AF_INET6;
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate 	if (af == AF_INET) {
7810Sstevel@tonic-gate 		if (strncmp(ruaddr, "0.0.0.0.", strlen("0.0.0.0.")) == 0)
7820Sstevel@tonic-gate 			/* thats me: return the way it is */
7830Sstevel@tonic-gate 			return (strdup(uaddr));
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 		/*
7860Sstevel@tonic-gate 		 * Convert remote uaddr into an in_addr so that we can compare
7870Sstevel@tonic-gate 		 * to it.  Shave off last two dotted-decimal values.
7880Sstevel@tonic-gate 		 */
7890Sstevel@tonic-gate 		for (cp = ruaddr, j = 0; j < 4; j++, cp++)
7900Sstevel@tonic-gate 			if ((cp = strchr(cp, '.')) == NULL)
7910Sstevel@tonic-gate 				break;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 		if (cp != NULL)
7940Sstevel@tonic-gate 			*--cp = '\0';	/* null out the dot after the IP addr */
7950Sstevel@tonic-gate 		else {
7960Sstevel@tonic-gate 			_nderror = ND_NOHOST;
7970Sstevel@tonic-gate 			return (NULL);
7980Sstevel@tonic-gate 		}
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 		clientaddr.s_addr = inet_addr(ruaddr);
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate #ifdef DEBUG
8030Sstevel@tonic-gate 		(void) printf("client's address is %s and %s\n",
8040Sstevel@tonic-gate 			ruaddr, inet_ntoa(clientaddr));
8050Sstevel@tonic-gate #endif
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 		/* We know cp is not NULL due to the check above */
8080Sstevel@tonic-gate 		*cp = '.';	/* Put the dot back in the IP addr */
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 		(void) time(&curtime);
8110Sstevel@tonic-gate 		if ((curtime - last_updated) >= if_cache_refresh_time) {
8120Sstevel@tonic-gate 			/*
8130Sstevel@tonic-gate 			 * Cache needs to be refreshed.
8140Sstevel@tonic-gate 			 */
8150Sstevel@tonic-gate 			if (!update_if_cache())
8160Sstevel@tonic-gate 				return (NULL);
8170Sstevel@tonic-gate 		}
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 		/*
8200Sstevel@tonic-gate 		 * Find the best match now.
8210Sstevel@tonic-gate 		 */
8220Sstevel@tonic-gate 		(void) rw_rdlock(&iflock);
8230Sstevel@tonic-gate 		bestmatch.s_addr = get_best_match(clientaddr);
8240Sstevel@tonic-gate 		(void) rw_unlock(&iflock);
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 		if (bestmatch.s_addr)
8270Sstevel@tonic-gate 			_nderror = ND_OK;
8280Sstevel@tonic-gate 		else {
8290Sstevel@tonic-gate 			_nderror = ND_NOHOST;
8300Sstevel@tonic-gate 			return (NULL);
8310Sstevel@tonic-gate 		}
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 		/* prepare the reply */
8340Sstevel@tonic-gate 		(void) memset(tmp, '\0', sizeof (tmp));
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 		/* reply consists of the IP addr of the closest interface */
8370Sstevel@tonic-gate 		(void) strcpy(tmp, inet_ntoa(bestmatch));
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 		/*
8400Sstevel@tonic-gate 		 * ... and the port number part (last two dotted-decimal values)
8410Sstevel@tonic-gate 		 * of uaddr
8420Sstevel@tonic-gate 		 */
8430Sstevel@tonic-gate 		for (cp = uaddr, j = 0; j < 4; j++, cp++)
8440Sstevel@tonic-gate 			cp = strchr(cp, '.');
8450Sstevel@tonic-gate 		(void) strcat(tmp, --cp);
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	} else {
8480Sstevel@tonic-gate 		/* IPv6 */
8490Sstevel@tonic-gate 		char *dot;
8500Sstevel@tonic-gate 		char *truaddr;
8510Sstevel@tonic-gate 		struct sockaddr_in6 sa;
8520Sstevel@tonic-gate 		struct sockaddr_in6 server_addr;
8530Sstevel@tonic-gate 		union any_in_addr in_addr, out_addr;
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 		if (strncmp(ruaddr, "::", strlen("::")) == 0)
8560Sstevel@tonic-gate 			if (*(ruaddr + strlen("::")) == '\0')
8570Sstevel@tonic-gate 				/* thats me: return the way it is */
8580Sstevel@tonic-gate 				return (strdup(uaddr));
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 		bzero(&sa, sizeof (sa));
8610Sstevel@tonic-gate 		bzero(&server_addr, sizeof (server_addr));
8620Sstevel@tonic-gate 		truaddr = &tmp[0];
863*132Srobinson 		(void) strcpy(truaddr, ruaddr);
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 		/*
8660Sstevel@tonic-gate 		 * now extract the server ip address from
8670Sstevel@tonic-gate 		 * the address supplied by client.  It can be
8680Sstevel@tonic-gate 		 * client's own IP address.
8690Sstevel@tonic-gate 		 */
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 		if ((dot = strrchr(truaddr, '.')) != 0) {
8720Sstevel@tonic-gate 			*dot = '\0';
8730Sstevel@tonic-gate 			if ((dot = strrchr(truaddr, '.')) != 0)
8740Sstevel@tonic-gate 				*dot = '\0';
8750Sstevel@tonic-gate 		}
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 		if (dot == 0) {
8780Sstevel@tonic-gate 			_nderror = ND_NOHOST;
8790Sstevel@tonic-gate 			return (NULL);
8800Sstevel@tonic-gate 		}
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 		if (inet_pton(af, truaddr, sa.sin6_addr.s6_addr)
8830Sstevel@tonic-gate 		    != 1) {
8840Sstevel@tonic-gate 			_nderror = ND_NOHOST;
8850Sstevel@tonic-gate 			return (NULL);
8860Sstevel@tonic-gate 		}
8870Sstevel@tonic-gate 
8880Sstevel@tonic-gate 		in_addr.addr6 = sa.sin6_addr;
8890Sstevel@tonic-gate 		sa.sin6_family = AF_INET6;
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 		/* is it my IP address */
8920Sstevel@tonic-gate 		if (!is_myself(&sa)) {
8930Sstevel@tonic-gate 			/* have the kernel select one for me */
8940Sstevel@tonic-gate 			if (select_server_addr(&in_addr, af, &out_addr) ==
8950Sstevel@tonic-gate 			    FALSE)
8960Sstevel@tonic-gate 				return (NULL);
8970Sstevel@tonic-gate 			server_addr.sin6_addr = out_addr.addr6;
8980Sstevel@tonic-gate 		}
8990Sstevel@tonic-gate 		else
900*132Srobinson 			(void) memcpy(&server_addr, &sa,
901*132Srobinson 						sizeof (struct sockaddr_in6));
9020Sstevel@tonic-gate #ifdef DEBUG
9030Sstevel@tonic-gate 		printf("%s\n", inet_ntop(af, out_addr.addr6.s6_addr,
9040Sstevel@tonic-gate 			tmp, sizeof (tmp)));
9050Sstevel@tonic-gate #endif
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 		if (inet_ntop(af, server_addr.sin6_addr.s6_addr,
9080Sstevel@tonic-gate 			tmp, sizeof (tmp)) == NULL) {
9090Sstevel@tonic-gate 			_nderror = ND_NOHOST;
9100Sstevel@tonic-gate 			return (NULL);
9110Sstevel@tonic-gate 		}
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 		/* now extract the port info */
9140Sstevel@tonic-gate 		if ((dot = strrchr(uaddr, '.')) != 0) {
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 			char *p;
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 			p = --dot;
9190Sstevel@tonic-gate 			while (*p-- != '.');
9200Sstevel@tonic-gate 			p++;
921*132Srobinson 			(void) strcat(tmp + strlen(tmp), p);
9220Sstevel@tonic-gate 			_nderror = ND_OK;
9230Sstevel@tonic-gate 		} else {
9240Sstevel@tonic-gate 			_nderror = ND_NOHOST;
9250Sstevel@tonic-gate 			return (NULL);
9260Sstevel@tonic-gate 		}
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	}
9290Sstevel@tonic-gate 	return (strdup(tmp));
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate static int
933*132Srobinson bindresvport(struct netconfig *nconf, int fd, struct netbuf *addr)
9340Sstevel@tonic-gate {
9350Sstevel@tonic-gate 	int res;
9360Sstevel@tonic-gate 	struct sockaddr_in myaddr;
9370Sstevel@tonic-gate 	struct sockaddr_in6 myaddr6;
9380Sstevel@tonic-gate 	struct sockaddr_in *sin;
9390Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
9400Sstevel@tonic-gate 	int i;
9410Sstevel@tonic-gate 	struct t_bind tbindstr, *tres;
9420Sstevel@tonic-gate 	struct t_info tinfo;
9430Sstevel@tonic-gate 	struct t_optmgmt req, resp;
9440Sstevel@tonic-gate 	struct opthdr *opt;
9450Sstevel@tonic-gate 	int reqbuf[64/sizeof (int)];
9460Sstevel@tonic-gate 	int *optval;
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	union {
9490Sstevel@tonic-gate 		struct sockaddr_in *sin;
9500Sstevel@tonic-gate 		struct sockaddr_in6 *sin6;
9510Sstevel@tonic-gate 		char *buf;
9520Sstevel@tonic-gate 	} u;
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	_nderror = ND_SYSTEM;
9550Sstevel@tonic-gate 	if (geteuid()) {
9560Sstevel@tonic-gate 		errno = EACCES;
9570Sstevel@tonic-gate 		return (-1);
9580Sstevel@tonic-gate 	}
9590Sstevel@tonic-gate 	if ((i = t_getstate(fd)) != T_UNBND) {
9600Sstevel@tonic-gate 		if (t_errno == TBADF)
9610Sstevel@tonic-gate 			errno = EBADF;
9620Sstevel@tonic-gate 		if (i != -1)
9630Sstevel@tonic-gate 			errno = EISCONN;
9640Sstevel@tonic-gate 		return (-1);
9650Sstevel@tonic-gate 	}
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
9680Sstevel@tonic-gate 		if (addr == NULL) {
9690Sstevel@tonic-gate 			sin = &myaddr;
970*132Srobinson 			(void) memset(sin, 0, sizeof (*sin));
9710Sstevel@tonic-gate 			sin->sin_family = AF_INET;
9720Sstevel@tonic-gate 			u.buf = (char *)sin;
9730Sstevel@tonic-gate 		} else
9740Sstevel@tonic-gate 			u.buf = (char *)addr->buf;
9750Sstevel@tonic-gate 	} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
9760Sstevel@tonic-gate 		if (addr == NULL) {
9770Sstevel@tonic-gate 			sin6 = &myaddr6;
978*132Srobinson 			(void) memset(sin6, 0, sizeof (*sin6));
9790Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
9800Sstevel@tonic-gate 			u.buf = (char *)sin6;
9810Sstevel@tonic-gate 		} else
9820Sstevel@tonic-gate 			u.buf = addr->buf;
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 	} else {
9850Sstevel@tonic-gate 		errno = EPFNOSUPPORT;
9860Sstevel@tonic-gate 		return (-1);
9870Sstevel@tonic-gate 	}
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	/* Transform sockaddr_in to netbuf */
9900Sstevel@tonic-gate 	if (t_getinfo(fd, &tinfo) == -1)
9910Sstevel@tonic-gate 		return (-1);
992*132Srobinson 	/* LINTED pointer cast */
9930Sstevel@tonic-gate 	tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
9940Sstevel@tonic-gate 	if (tres == NULL) {
9950Sstevel@tonic-gate 		_nderror = ND_NOMEM;
9960Sstevel@tonic-gate 		return (-1);
9970Sstevel@tonic-gate 	}
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 	tbindstr.qlen = 0; /* Always 0; user should change if he wants to */
10000Sstevel@tonic-gate 	tbindstr.addr.buf = (char *)u.buf;
10010Sstevel@tonic-gate 	tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	/*
10040Sstevel@tonic-gate 	 * Use *_ANONPRIVBIND to ask the kernel to pick a port in the
10050Sstevel@tonic-gate 	 * priviledged range for us.
10060Sstevel@tonic-gate 	 */
10070Sstevel@tonic-gate 	opt = (struct opthdr *)reqbuf;
10080Sstevel@tonic-gate 	if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
10090Sstevel@tonic-gate 		opt->level = IPPROTO_TCP;
10100Sstevel@tonic-gate 		opt->name = TCP_ANONPRIVBIND;
10110Sstevel@tonic-gate 	} else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
10120Sstevel@tonic-gate 		opt->level = IPPROTO_UDP;
10130Sstevel@tonic-gate 		opt->name = UDP_ANONPRIVBIND;
10140Sstevel@tonic-gate 	} else {
10150Sstevel@tonic-gate 		errno = EPROTONOSUPPORT;
10160Sstevel@tonic-gate 		(void) t_free((char *)tres, T_BIND);
10170Sstevel@tonic-gate 		return (-1);
10180Sstevel@tonic-gate 	}
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 	opt->len = sizeof (int);
10210Sstevel@tonic-gate 	req.flags = T_NEGOTIATE;
10220Sstevel@tonic-gate 	req.opt.len = sizeof (struct opthdr) + opt->len;
10230Sstevel@tonic-gate 	req.opt.buf = (char *)opt;
1024*132Srobinson 	/* LINTED pointer cast */
10250Sstevel@tonic-gate 	optval = (int *)((char *)reqbuf + sizeof (struct opthdr));
10260Sstevel@tonic-gate 	*optval = 1;
10270Sstevel@tonic-gate 	resp.flags = 0;
10280Sstevel@tonic-gate 	resp.opt.buf = (char *)reqbuf;
10290Sstevel@tonic-gate 	resp.opt.maxlen = sizeof (reqbuf);
10300Sstevel@tonic-gate 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
10310Sstevel@tonic-gate 		(void) t_free((char *)tres, T_BIND);
10320Sstevel@tonic-gate 		return (-1);
10330Sstevel@tonic-gate 	}
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	if (u.sin->sin_family == AF_INET)
10360Sstevel@tonic-gate 		u.sin->sin_port = htons(0);
10370Sstevel@tonic-gate 	else
10380Sstevel@tonic-gate 		u.sin6->sin6_port = htons(0);
10390Sstevel@tonic-gate 	res = t_bind(fd, &tbindstr, tres);
10400Sstevel@tonic-gate 	if (res != 0) {
10410Sstevel@tonic-gate 		if (t_errno == TNOADDR) {
10420Sstevel@tonic-gate 			_nderror = ND_FAILCTRL;
10430Sstevel@tonic-gate 			res = 1;
10440Sstevel@tonic-gate 		}
10450Sstevel@tonic-gate 	} else {
10460Sstevel@tonic-gate 		_nderror = ND_OK;
10470Sstevel@tonic-gate 	}
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	/*
10500Sstevel@tonic-gate 	 * Always turn off the option when we are done.  Note that by doing
10510Sstevel@tonic-gate 	 * this, if the caller has set this option before calling
10520Sstevel@tonic-gate 	 * bindresvport(), it will be unset.  Better be safe...
10530Sstevel@tonic-gate 	 */
10540Sstevel@tonic-gate 	 *optval = 0;
10550Sstevel@tonic-gate 	resp.flags = 0;
10560Sstevel@tonic-gate 	resp.opt.buf = (char *)reqbuf;
10570Sstevel@tonic-gate 	resp.opt.maxlen = sizeof (reqbuf);
10580Sstevel@tonic-gate 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
10590Sstevel@tonic-gate 		(void) t_free((char *)tres, T_BIND);
10600Sstevel@tonic-gate 		if (res == 0)
10610Sstevel@tonic-gate 			(void) t_unbind(fd);
10620Sstevel@tonic-gate 		_nderror = ND_FAILCTRL;
10630Sstevel@tonic-gate 		return (-1);
10640Sstevel@tonic-gate 	}
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate 	(void) t_free((char *)tres, T_BIND);
10670Sstevel@tonic-gate 	return (res);
10680Sstevel@tonic-gate }
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate static int
1071*132Srobinson checkresvport(struct netbuf *addr)
10720Sstevel@tonic-gate {
10730Sstevel@tonic-gate 	struct sockaddr_in *sin;
10740Sstevel@tonic-gate 	unsigned short port;
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	if (addr == NULL) {
10770Sstevel@tonic-gate 		_nderror = ND_FAILCTRL;
10780Sstevel@tonic-gate 		return (-1);
10790Sstevel@tonic-gate 	}
10800Sstevel@tonic-gate 	/*
10810Sstevel@tonic-gate 	 * Still works for IPv6 since the first two memebers of
10820Sstevel@tonic-gate 	 * both address structure point to family and port # respectively
10830Sstevel@tonic-gate 	 */
1084*132Srobinson 	/* LINTED pointer cast */
10850Sstevel@tonic-gate 	sin = (struct sockaddr_in *)(addr->buf);
10860Sstevel@tonic-gate 	port = ntohs(sin->sin_port);
10870Sstevel@tonic-gate 	if (port < IPPORT_RESERVED)
10880Sstevel@tonic-gate 		return (0);
10890Sstevel@tonic-gate 	return (1);
10900Sstevel@tonic-gate }
1091