1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  *
26*7c478bd9Sstevel@tonic-gate  * lib/libnsl/nss/netdir_inet_sundry.c
27*7c478bd9Sstevel@tonic-gate  *
28*7c478bd9Sstevel@tonic-gate  * This file contains inet-specific implementations of netdir_options,
29*7c478bd9Sstevel@tonic-gate  * uaddr2taddr, and taddr2uaddr. These implementations
30*7c478bd9Sstevel@tonic-gate  * used to be in both tcpip.so and switch.so (identical copies).
31*7c478bd9Sstevel@tonic-gate  * Since we got rid of those, and also it's a good idea to build-in
32*7c478bd9Sstevel@tonic-gate  * inet-specific implementations in one place, we decided to put
33*7c478bd9Sstevel@tonic-gate  * them in this file with a not-so glorious name. These are INET-SPECIFIC
34*7c478bd9Sstevel@tonic-gate  * only, and will not be used for non-inet transports or by third-parties
35*7c478bd9Sstevel@tonic-gate  * that decide to provide their own nametoaddr libs for inet transports
36*7c478bd9Sstevel@tonic-gate  * (they are on their own for these as well => they get flexibility).
37*7c478bd9Sstevel@tonic-gate  *
38*7c478bd9Sstevel@tonic-gate  * Copied mostly from erstwhile lib/nametoaddr/tcpip/tcpip.c.
39*7c478bd9Sstevel@tonic-gate  */
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate #include "mt.h"
44*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
45*7c478bd9Sstevel@tonic-gate #include <stdio.h>
46*7c478bd9Sstevel@tonic-gate #include <string.h>
47*7c478bd9Sstevel@tonic-gate #include <unistd.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
50*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
51*7c478bd9Sstevel@tonic-gate #include <errno.h>
52*7c478bd9Sstevel@tonic-gate #include <thread.h>
53*7c478bd9Sstevel@tonic-gate #include <netconfig.h>
54*7c478bd9Sstevel@tonic-gate #include <netdir.h>
55*7c478bd9Sstevel@tonic-gate #include <nss_netdir.h>
56*7c478bd9Sstevel@tonic-gate #include <tiuser.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/socket.h>
58*7c478bd9Sstevel@tonic-gate #include <net/if.h>
59*7c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/fcntl.h>
61*7c478bd9Sstevel@tonic-gate #include <netinet/in.h>
62*7c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
63*7c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
64*7c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
65*7c478bd9Sstevel@tonic-gate #include <rpc/types.h>
66*7c478bd9Sstevel@tonic-gate #include <rpc/trace.h>
67*7c478bd9Sstevel@tonic-gate #include <rpc/rpc_com.h>
68*7c478bd9Sstevel@tonic-gate #include <syslog.h>
69*7c478bd9Sstevel@tonic-gate #include <values.h>
70*7c478bd9Sstevel@tonic-gate #include <limits.h>
71*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
72*7c478bd9Sstevel@tonic-gate #include <stdio.h>
73*7c478bd9Sstevel@tonic-gate #endif
74*7c478bd9Sstevel@tonic-gate #include <nss_dbdefs.h>
75*7c478bd9Sstevel@tonic-gate #include "nss.h"
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate #define	MAXIFS 32
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate /*
80*7c478bd9Sstevel@tonic-gate  * Extracted from socketvar.h
81*7c478bd9Sstevel@tonic-gate  */
82*7c478bd9Sstevel@tonic-gate #define	SOV_DEFAULT	1	/* Select based on so_default_version */
83*7c478bd9Sstevel@tonic-gate #define	SOV_SOCKBSD	3	/* Socket with no streams operations */
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate extern int _so_socket();
86*7c478bd9Sstevel@tonic-gate extern int _so_connect();
87*7c478bd9Sstevel@tonic-gate extern int _so_getsockname();
88*7c478bd9Sstevel@tonic-gate extern int bzero();
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate static char *inet_netdir_mergeaddr(struct netconfig *, char *, char *);
92*7c478bd9Sstevel@tonic-gate static int bindresvport(struct netconfig *, int, struct netbuf *);
93*7c478bd9Sstevel@tonic-gate static int checkresvport(struct netbuf *);
94*7c478bd9Sstevel@tonic-gate static struct netbuf *ip_uaddr2taddr(char *);
95*7c478bd9Sstevel@tonic-gate static struct netbuf *ipv6_uaddr2taddr(char *);
96*7c478bd9Sstevel@tonic-gate 
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate extern char *inet_ntoa_r(struct in_addr, char *);
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate int
101*7c478bd9Sstevel@tonic-gate __inet_netdir_options(tp, opts, fd, par)
102*7c478bd9Sstevel@tonic-gate 	struct netconfig *tp;
103*7c478bd9Sstevel@tonic-gate 	int opts;
104*7c478bd9Sstevel@tonic-gate 	int fd;
105*7c478bd9Sstevel@tonic-gate 	char *par;
106*7c478bd9Sstevel@tonic-gate {
107*7c478bd9Sstevel@tonic-gate 	struct nd_mergearg *ma;
108*7c478bd9Sstevel@tonic-gate 
109*7c478bd9Sstevel@tonic-gate 	switch (opts) {
110*7c478bd9Sstevel@tonic-gate 	case ND_SET_BROADCAST:
111*7c478bd9Sstevel@tonic-gate 		/* Every one is allowed to broadcast without asking */
112*7c478bd9Sstevel@tonic-gate 		return (ND_OK);
113*7c478bd9Sstevel@tonic-gate 	case ND_SET_RESERVEDPORT:	/* bind to a resered port */
114*7c478bd9Sstevel@tonic-gate 		return (bindresvport(tp, fd, (struct netbuf *)par));
115*7c478bd9Sstevel@tonic-gate 	case ND_CHECK_RESERVEDPORT:	/* check if reserved prot */
116*7c478bd9Sstevel@tonic-gate 		return (checkresvport((struct netbuf *)par));
117*7c478bd9Sstevel@tonic-gate 	case ND_MERGEADDR:	/* Merge two addresses */
118*7c478bd9Sstevel@tonic-gate 		ma = (struct nd_mergearg *)(par);
119*7c478bd9Sstevel@tonic-gate 		ma->m_uaddr = inet_netdir_mergeaddr(tp, ma->c_uaddr,
120*7c478bd9Sstevel@tonic-gate 		    ma->s_uaddr);
121*7c478bd9Sstevel@tonic-gate 		return (_nderror);
122*7c478bd9Sstevel@tonic-gate 	default:
123*7c478bd9Sstevel@tonic-gate 		return (ND_NOCTRL);
124*7c478bd9Sstevel@tonic-gate 	}
125*7c478bd9Sstevel@tonic-gate }
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate /*
129*7c478bd9Sstevel@tonic-gate  * This routine will convert a TCP/IP internal format address
130*7c478bd9Sstevel@tonic-gate  * into a "universal" format address. In our case it prints out the
131*7c478bd9Sstevel@tonic-gate  * decimal dot equivalent. h1.h2.h3.h4.p1.p2 where h1-h4 are the host
132*7c478bd9Sstevel@tonic-gate  * address and p1-p2 are the port number.
133*7c478bd9Sstevel@tonic-gate  */
134*7c478bd9Sstevel@tonic-gate char *
135*7c478bd9Sstevel@tonic-gate __inet_taddr2uaddr(tp, addr)
136*7c478bd9Sstevel@tonic-gate 	struct netconfig	*tp;	/* the transport provider */
137*7c478bd9Sstevel@tonic-gate 	struct netbuf		*addr;	/* the netbuf struct */
138*7c478bd9Sstevel@tonic-gate {
139*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in	*sa;	/* our internal format */
140*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6	*sa6;	/* our internal format */
141*7c478bd9Sstevel@tonic-gate 	char			tmp[RPC_INET6_MAXUADDRSIZE];
142*7c478bd9Sstevel@tonic-gate 	unsigned short		myport;
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 	if (addr == NULL || tp == NULL || addr->buf == NULL) {
145*7c478bd9Sstevel@tonic-gate 		_nderror = ND_BADARG;
146*7c478bd9Sstevel@tonic-gate 		return (NULL);
147*7c478bd9Sstevel@tonic-gate 	}
148*7c478bd9Sstevel@tonic-gate 	if (strcmp(tp->nc_protofmly, NC_INET) == 0) {
149*7c478bd9Sstevel@tonic-gate 		sa = (struct sockaddr_in *)(addr->buf);
150*7c478bd9Sstevel@tonic-gate 		myport = ntohs(sa->sin_port);
151*7c478bd9Sstevel@tonic-gate 		inet_ntoa_r(sa->sin_addr, tmp);
152*7c478bd9Sstevel@tonic-gate 	} else {
153*7c478bd9Sstevel@tonic-gate 		sa6 = (struct sockaddr_in6 *)(addr->buf);
154*7c478bd9Sstevel@tonic-gate 		myport = ntohs(sa6->sin6_port);
155*7c478bd9Sstevel@tonic-gate 		if (inet_ntop(AF_INET6, (void *)sa6->sin6_addr.s6_addr,
156*7c478bd9Sstevel@tonic-gate 			tmp, sizeof (tmp)) == 0) {
157*7c478bd9Sstevel@tonic-gate 			_nderror = ND_BADARG;
158*7c478bd9Sstevel@tonic-gate 			return (NULL);
159*7c478bd9Sstevel@tonic-gate 		}
160*7c478bd9Sstevel@tonic-gate 	}
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate 	(void) sprintf(tmp + strlen(tmp), ".%d.%d", myport >> 8, myport & 255);
163*7c478bd9Sstevel@tonic-gate 	return (strdup(tmp));	/* Doesn't return static data ! */
164*7c478bd9Sstevel@tonic-gate }
165*7c478bd9Sstevel@tonic-gate 
166*7c478bd9Sstevel@tonic-gate /*
167*7c478bd9Sstevel@tonic-gate  * This internal routine will convert one of those "universal" addresses
168*7c478bd9Sstevel@tonic-gate  * to the internal format used by the Sun TLI TCP/IP provider.
169*7c478bd9Sstevel@tonic-gate  */
170*7c478bd9Sstevel@tonic-gate struct netbuf *
171*7c478bd9Sstevel@tonic-gate __inet_uaddr2taddr(tp, addr)
172*7c478bd9Sstevel@tonic-gate 	struct netconfig	*tp;	/* the transport provider */
173*7c478bd9Sstevel@tonic-gate 	char			*addr;	/* the address */
174*7c478bd9Sstevel@tonic-gate {
175*7c478bd9Sstevel@tonic-gate 	if (!addr || !tp) {
176*7c478bd9Sstevel@tonic-gate 		_nderror = ND_BADARG;
177*7c478bd9Sstevel@tonic-gate 		return (NULL);
178*7c478bd9Sstevel@tonic-gate 	}
179*7c478bd9Sstevel@tonic-gate 	if (strcmp(tp->nc_protofmly, NC_INET) == 0)
180*7c478bd9Sstevel@tonic-gate 		return (ip_uaddr2taddr(addr));
181*7c478bd9Sstevel@tonic-gate 	else
182*7c478bd9Sstevel@tonic-gate 		return (ipv6_uaddr2taddr(addr));
183*7c478bd9Sstevel@tonic-gate }
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate static struct netbuf *
186*7c478bd9Sstevel@tonic-gate ip_uaddr2taddr(char *addr)
187*7c478bd9Sstevel@tonic-gate {
188*7c478bd9Sstevel@tonic-gate 
189*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in	*sa;
190*7c478bd9Sstevel@tonic-gate 	uint32_t		inaddr;
191*7c478bd9Sstevel@tonic-gate 	unsigned short		inport;
192*7c478bd9Sstevel@tonic-gate 	int			h1, h2, h3, h4, p1, p2;
193*7c478bd9Sstevel@tonic-gate 	struct netbuf		*result;
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate 	result = malloc(sizeof (struct netbuf));
196*7c478bd9Sstevel@tonic-gate 	if (!result) {
197*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
198*7c478bd9Sstevel@tonic-gate 		return (NULL);
199*7c478bd9Sstevel@tonic-gate 	}
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 	sa = calloc(1, sizeof (*sa));
202*7c478bd9Sstevel@tonic-gate 
203*7c478bd9Sstevel@tonic-gate 	if (!sa) {
204*7c478bd9Sstevel@tonic-gate 		free(result);
205*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
206*7c478bd9Sstevel@tonic-gate 		return (NULL);
207*7c478bd9Sstevel@tonic-gate 	}
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	result->buf = (char *)(sa);
210*7c478bd9Sstevel@tonic-gate 	result->maxlen = sizeof (struct sockaddr_in);
211*7c478bd9Sstevel@tonic-gate 	result->len = sizeof (struct sockaddr_in);
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 	/* XXX there is probably a better way to do this. */
214*7c478bd9Sstevel@tonic-gate 	if (sscanf(addr, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4,
215*7c478bd9Sstevel@tonic-gate 	    &p1, &p2) != 6) {
216*7c478bd9Sstevel@tonic-gate 		free(result);
217*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NO_RECOVERY;
218*7c478bd9Sstevel@tonic-gate 		return (NULL);
219*7c478bd9Sstevel@tonic-gate 	}
220*7c478bd9Sstevel@tonic-gate 
221*7c478bd9Sstevel@tonic-gate 	/* convert the host address first */
222*7c478bd9Sstevel@tonic-gate 	inaddr = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;
223*7c478bd9Sstevel@tonic-gate 	sa->sin_addr.s_addr = htonl(inaddr);
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 	/* convert the port */
226*7c478bd9Sstevel@tonic-gate 	inport = (p1 << 8) + p2;
227*7c478bd9Sstevel@tonic-gate 	sa->sin_port = htons(inport);
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	sa->sin_family = AF_INET;
230*7c478bd9Sstevel@tonic-gate 
231*7c478bd9Sstevel@tonic-gate 	return (result);
232*7c478bd9Sstevel@tonic-gate }
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate static struct netbuf *
235*7c478bd9Sstevel@tonic-gate ipv6_uaddr2taddr(char	*addr)
236*7c478bd9Sstevel@tonic-gate {
237*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6	*sa;
238*7c478bd9Sstevel@tonic-gate 	unsigned short		inport;
239*7c478bd9Sstevel@tonic-gate 	int	 p1, p2;
240*7c478bd9Sstevel@tonic-gate 	struct netbuf		*result;
241*7c478bd9Sstevel@tonic-gate 	char tmpaddr[RPC_INET6_MAXUADDRSIZE];
242*7c478bd9Sstevel@tonic-gate 	char *dot;
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 	result = malloc(sizeof (struct netbuf));
245*7c478bd9Sstevel@tonic-gate 	if (!result) {
246*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
247*7c478bd9Sstevel@tonic-gate 		return (NULL);
248*7c478bd9Sstevel@tonic-gate 	}
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate 	sa = calloc(1, sizeof (struct sockaddr_in6));
251*7c478bd9Sstevel@tonic-gate 	if (!sa) {
252*7c478bd9Sstevel@tonic-gate 		free(result);
253*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
254*7c478bd9Sstevel@tonic-gate 		return (NULL);
255*7c478bd9Sstevel@tonic-gate 	}
256*7c478bd9Sstevel@tonic-gate 	result->buf = (char *)(sa);
257*7c478bd9Sstevel@tonic-gate 	result->maxlen = sizeof (struct sockaddr_in6);
258*7c478bd9Sstevel@tonic-gate 	result->len = sizeof (struct sockaddr_in6);
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 	/* retrieve the ipv6 address and port info */
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate 	if (strlen(addr) > sizeof (tmpaddr) - 1) {
263*7c478bd9Sstevel@tonic-gate 		free(result);
264*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
265*7c478bd9Sstevel@tonic-gate 		return (NULL);
266*7c478bd9Sstevel@tonic-gate 	}
267*7c478bd9Sstevel@tonic-gate 
268*7c478bd9Sstevel@tonic-gate 	strcpy(tmpaddr, addr);
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	if ((dot = strrchr(tmpaddr, '.')) != 0) {
271*7c478bd9Sstevel@tonic-gate 		*dot = '\0';
272*7c478bd9Sstevel@tonic-gate 		p2 = atoi(dot+1);
273*7c478bd9Sstevel@tonic-gate 		if ((dot = strrchr(tmpaddr, '.')) != 0) {
274*7c478bd9Sstevel@tonic-gate 			*dot = '\0';
275*7c478bd9Sstevel@tonic-gate 			p1 = atoi(dot+1);
276*7c478bd9Sstevel@tonic-gate 		}
277*7c478bd9Sstevel@tonic-gate 	}
278*7c478bd9Sstevel@tonic-gate 
279*7c478bd9Sstevel@tonic-gate 	if (dot == 0) {
280*7c478bd9Sstevel@tonic-gate 		free(result);
281*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
282*7c478bd9Sstevel@tonic-gate 		return (NULL);
283*7c478bd9Sstevel@tonic-gate 	}
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate 	if (inet_pton(AF_INET6, tmpaddr, sa->sin6_addr.s6_addr) == 0) {
286*7c478bd9Sstevel@tonic-gate 		free(result);
287*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
288*7c478bd9Sstevel@tonic-gate 		return (NULL);
289*7c478bd9Sstevel@tonic-gate 	}
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 	/* convert the port */
292*7c478bd9Sstevel@tonic-gate 	inport = (p1 << 8) + p2;
293*7c478bd9Sstevel@tonic-gate 	sa->sin6_port = htons(inport);
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 	sa->sin6_family = AF_INET6;
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 	return (result);
298*7c478bd9Sstevel@tonic-gate }
299*7c478bd9Sstevel@tonic-gate 
300*7c478bd9Sstevel@tonic-gate /*
301*7c478bd9Sstevel@tonic-gate  * Interface caching routines.  The cache is refreshed every
302*7c478bd9Sstevel@tonic-gate  * IF_CACHE_REFRESH_TIME seconds.  A read-write lock is used to
303*7c478bd9Sstevel@tonic-gate  * protect the cache.
304*7c478bd9Sstevel@tonic-gate  */
305*7c478bd9Sstevel@tonic-gate #define	IF_CACHE_REFRESH_TIME 10
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate static int if_cache_refresh_time = IF_CACHE_REFRESH_TIME;
308*7c478bd9Sstevel@tonic-gate static rwlock_t iflock = DEFAULTRWLOCK;
309*7c478bd9Sstevel@tonic-gate static time_t last_updated = 0;		/* protected by iflock */
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate /*
312*7c478bd9Sstevel@tonic-gate  * Changing the data type of if_flags from uint_t to uint64_t to accomodate
313*7c478bd9Sstevel@tonic-gate  * extra flags. Refer <net/if.h> for the extra flags.
314*7c478bd9Sstevel@tonic-gate  */
315*7c478bd9Sstevel@tonic-gate typedef struct if_info_s {
316*7c478bd9Sstevel@tonic-gate 	struct in_addr if_netmask;	/* netmask in network order */
317*7c478bd9Sstevel@tonic-gate 	struct in_addr if_address;	/* address in network order */
318*7c478bd9Sstevel@tonic-gate 	uint64_t if_flags;		/* interface flags */
319*7c478bd9Sstevel@tonic-gate } if_info_t;
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate static if_info_t *if_info = NULL;	/* if cache, protected by iflock */
322*7c478bd9Sstevel@tonic-gate static int n_ifs = 0;			/* number of cached interfaces */
323*7c478bd9Sstevel@tonic-gate static int numifs_last = 0;		/* number of interfaces last seen */
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate /*
326*7c478bd9Sstevel@tonic-gate  * Builds the interface cache.  Write lock on iflock is needed
327*7c478bd9Sstevel@tonic-gate  * for calling this routine.  It sets _nderror for error returns.
328*7c478bd9Sstevel@tonic-gate  * Returns TRUE if successful, FALSE otherwise.
329*7c478bd9Sstevel@tonic-gate  * Changing the structures ifreq and ifconf to lifreq and lifconf to
330*7c478bd9Sstevel@tonic-gate  * have larger flag field. This is to accomodate the extra flags associated
331*7c478bd9Sstevel@tonic-gate  * with the interface. Also introducing lifn which will contain the number
332*7c478bd9Sstevel@tonic-gate  * of IPV4 interfaces present.
333*7c478bd9Sstevel@tonic-gate  */
334*7c478bd9Sstevel@tonic-gate static bool_t
335*7c478bd9Sstevel@tonic-gate get_if_info()
336*7c478bd9Sstevel@tonic-gate {
337*7c478bd9Sstevel@tonic-gate 	size_t		needed;
338*7c478bd9Sstevel@tonic-gate 	struct lifreq	*buf = NULL;
339*7c478bd9Sstevel@tonic-gate 	int		numifs;
340*7c478bd9Sstevel@tonic-gate 	struct lifconf  lifc;
341*7c478bd9Sstevel@tonic-gate 	struct lifreq   *lifr;
342*7c478bd9Sstevel@tonic-gate 	struct lifnum   lifn;
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate 	lifn.lifn_family = AF_INET;
345*7c478bd9Sstevel@tonic-gate 	lifn.lifn_flags = 0;
346*7c478bd9Sstevel@tonic-gate getifnum:
347*7c478bd9Sstevel@tonic-gate 	if (nss_ioctl(AF_INET, SIOCGLIFNUM, &lifn) == -1) {
348*7c478bd9Sstevel@tonic-gate 		numifs = MAXIFS;
349*7c478bd9Sstevel@tonic-gate 	} else {
350*7c478bd9Sstevel@tonic-gate 		numifs = lifn.lifn_count;
351*7c478bd9Sstevel@tonic-gate 	}
352*7c478bd9Sstevel@tonic-gate 	/*
353*7c478bd9Sstevel@tonic-gate 	 * Add a small fudge factor in case interfaces are plumbed
354*7c478bd9Sstevel@tonic-gate 	 * between the SIOCGLIFNUM and SIOCGLIFCONF.
355*7c478bd9Sstevel@tonic-gate 	 */
356*7c478bd9Sstevel@tonic-gate 	needed = (numifs + 4) * sizeof (struct lifreq);
357*7c478bd9Sstevel@tonic-gate 	if (buf == NULL)
358*7c478bd9Sstevel@tonic-gate 		buf = malloc(needed);
359*7c478bd9Sstevel@tonic-gate 	else
360*7c478bd9Sstevel@tonic-gate 		buf = realloc(buf, needed);
361*7c478bd9Sstevel@tonic-gate 	if (buf == NULL) {
362*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
363*7c478bd9Sstevel@tonic-gate 		return (FALSE);
364*7c478bd9Sstevel@tonic-gate 	}
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 	lifc.lifc_family = AF_INET;
367*7c478bd9Sstevel@tonic-gate 	lifc.lifc_flags = 0;
368*7c478bd9Sstevel@tonic-gate 	lifc.lifc_len = needed;
369*7c478bd9Sstevel@tonic-gate 	lifc.lifc_buf = (char *)buf;
370*7c478bd9Sstevel@tonic-gate 	if (nss_ioctl(AF_INET, SIOCGLIFCONF, &lifc) == -1) {
371*7c478bd9Sstevel@tonic-gate 		/*
372*7c478bd9Sstevel@tonic-gate 		 * IP returns EINVAL if the buffer was too small to fit
373*7c478bd9Sstevel@tonic-gate 		 * all of the entries.  If that's the case, go back and
374*7c478bd9Sstevel@tonic-gate 		 * try again.
375*7c478bd9Sstevel@tonic-gate 		 */
376*7c478bd9Sstevel@tonic-gate 		if (errno == EINVAL)
377*7c478bd9Sstevel@tonic-gate 			goto getifnum;
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 		free(buf);
380*7c478bd9Sstevel@tonic-gate 		free(if_info);
381*7c478bd9Sstevel@tonic-gate 		if_info = NULL;
382*7c478bd9Sstevel@tonic-gate 		_nderror = ND_SYSTEM;
383*7c478bd9Sstevel@tonic-gate 		return (FALSE);
384*7c478bd9Sstevel@tonic-gate 	}
385*7c478bd9Sstevel@tonic-gate 	numifs = lifc.lifc_len / (int)sizeof (struct lifreq);
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 	if (if_info == NULL || numifs > numifs_last) {
388*7c478bd9Sstevel@tonic-gate 		if (if_info == NULL)
389*7c478bd9Sstevel@tonic-gate 			if_info = malloc(numifs * sizeof (if_info_t));
390*7c478bd9Sstevel@tonic-gate 		else
391*7c478bd9Sstevel@tonic-gate 			if_info = realloc(if_info, numifs * sizeof (if_info_t));
392*7c478bd9Sstevel@tonic-gate 		if (if_info == NULL) {
393*7c478bd9Sstevel@tonic-gate 			free(buf);
394*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOMEM;
395*7c478bd9Sstevel@tonic-gate 			return (FALSE);
396*7c478bd9Sstevel@tonic-gate 		}
397*7c478bd9Sstevel@tonic-gate 		numifs_last = numifs;
398*7c478bd9Sstevel@tonic-gate 	}
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate 	n_ifs = 0;
401*7c478bd9Sstevel@tonic-gate 	for (lifr = buf; lifr < (buf + numifs); lifr++) {
402*7c478bd9Sstevel@tonic-gate 		if (lifr->lifr_addr.ss_family != AF_INET)
403*7c478bd9Sstevel@tonic-gate 			continue;
404*7c478bd9Sstevel@tonic-gate 
405*7c478bd9Sstevel@tonic-gate 		if_info[n_ifs].if_address =
406*7c478bd9Sstevel@tonic-gate 			((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 		if (nss_ioctl(AF_INET, SIOCGLIFFLAGS, lifr) < 0)
409*7c478bd9Sstevel@tonic-gate 			continue;
410*7c478bd9Sstevel@tonic-gate 
411*7c478bd9Sstevel@tonic-gate 		if ((lifr->lifr_flags & IFF_UP) == 0)
412*7c478bd9Sstevel@tonic-gate 			continue;
413*7c478bd9Sstevel@tonic-gate 		if_info[n_ifs].if_flags = lifr->lifr_flags;
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 		if (nss_ioctl(AF_INET, SIOCGLIFNETMASK, lifr) < 0)
416*7c478bd9Sstevel@tonic-gate 			continue;
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 		if_info[n_ifs].if_netmask =
419*7c478bd9Sstevel@tonic-gate 			((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
420*7c478bd9Sstevel@tonic-gate 		n_ifs++;
421*7c478bd9Sstevel@tonic-gate 	}
422*7c478bd9Sstevel@tonic-gate 	free(buf);
423*7c478bd9Sstevel@tonic-gate 	return (TRUE);
424*7c478bd9Sstevel@tonic-gate }
425*7c478bd9Sstevel@tonic-gate 
426*7c478bd9Sstevel@tonic-gate 
427*7c478bd9Sstevel@tonic-gate /*
428*7c478bd9Sstevel@tonic-gate  * Update the interface cache based on last update time.
429*7c478bd9Sstevel@tonic-gate  */
430*7c478bd9Sstevel@tonic-gate static bool_t
431*7c478bd9Sstevel@tonic-gate update_if_cache()
432*7c478bd9Sstevel@tonic-gate {
433*7c478bd9Sstevel@tonic-gate 	time_t	curtime;
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 	(void) rw_wrlock(&iflock);
436*7c478bd9Sstevel@tonic-gate 	/*
437*7c478bd9Sstevel@tonic-gate 	 * Check if some other thread has beaten this one to it.
438*7c478bd9Sstevel@tonic-gate 	 */
439*7c478bd9Sstevel@tonic-gate 	(void) time(&curtime);
440*7c478bd9Sstevel@tonic-gate 	if ((curtime - last_updated) >= if_cache_refresh_time) {
441*7c478bd9Sstevel@tonic-gate 		if (!get_if_info()) {
442*7c478bd9Sstevel@tonic-gate 			(void) rw_unlock(&iflock);
443*7c478bd9Sstevel@tonic-gate 			return (FALSE);
444*7c478bd9Sstevel@tonic-gate 		}
445*7c478bd9Sstevel@tonic-gate 		(void) time(&last_updated);
446*7c478bd9Sstevel@tonic-gate 	}
447*7c478bd9Sstevel@tonic-gate 	(void) rw_unlock(&iflock);
448*7c478bd9Sstevel@tonic-gate 	return (TRUE);
449*7c478bd9Sstevel@tonic-gate }
450*7c478bd9Sstevel@tonic-gate 
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate /*
453*7c478bd9Sstevel@tonic-gate  * Given an IP address, check if this matches any of the interface
454*7c478bd9Sstevel@tonic-gate  * addresses.  If an error occurs, return FALSE so that the caller
455*7c478bd9Sstevel@tonic-gate  * will not assume that this address belongs to this machine.
456*7c478bd9Sstevel@tonic-gate  */
457*7c478bd9Sstevel@tonic-gate static bool_t
458*7c478bd9Sstevel@tonic-gate is_my_address(addr)
459*7c478bd9Sstevel@tonic-gate 	struct in_addr addr;		/* address in network order */
460*7c478bd9Sstevel@tonic-gate {
461*7c478bd9Sstevel@tonic-gate 	time_t		curtime;
462*7c478bd9Sstevel@tonic-gate 	if_info_t	*ifn;
463*7c478bd9Sstevel@tonic-gate 
464*7c478bd9Sstevel@tonic-gate 	(void) time(&curtime);
465*7c478bd9Sstevel@tonic-gate 	if ((curtime - last_updated) >= if_cache_refresh_time) {
466*7c478bd9Sstevel@tonic-gate 		/*
467*7c478bd9Sstevel@tonic-gate 		 * Cache needs to be refreshed.
468*7c478bd9Sstevel@tonic-gate 		 */
469*7c478bd9Sstevel@tonic-gate 		if (!update_if_cache())
470*7c478bd9Sstevel@tonic-gate 			return (FALSE);
471*7c478bd9Sstevel@tonic-gate 	}
472*7c478bd9Sstevel@tonic-gate 	(void) rw_rdlock(&iflock);
473*7c478bd9Sstevel@tonic-gate 	for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
474*7c478bd9Sstevel@tonic-gate 		if (addr.s_addr == ifn->if_address.s_addr) {
475*7c478bd9Sstevel@tonic-gate 			(void) rw_unlock(&iflock);
476*7c478bd9Sstevel@tonic-gate 			return (TRUE);
477*7c478bd9Sstevel@tonic-gate 		}
478*7c478bd9Sstevel@tonic-gate 	}
479*7c478bd9Sstevel@tonic-gate 	(void) rw_unlock(&iflock);
480*7c478bd9Sstevel@tonic-gate 	return (FALSE);
481*7c478bd9Sstevel@tonic-gate }
482*7c478bd9Sstevel@tonic-gate 
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate /*
485*7c478bd9Sstevel@tonic-gate  * Given a host name, check if it is this host.
486*7c478bd9Sstevel@tonic-gate  */
487*7c478bd9Sstevel@tonic-gate bool_t
488*7c478bd9Sstevel@tonic-gate __inet_netdir_is_my_host(host)
489*7c478bd9Sstevel@tonic-gate 	char		*host;
490*7c478bd9Sstevel@tonic-gate {
491*7c478bd9Sstevel@tonic-gate 	int		error;
492*7c478bd9Sstevel@tonic-gate 	char		buf[NSS_BUFLEN_HOSTS];
493*7c478bd9Sstevel@tonic-gate 	struct hostent	res, *h;
494*7c478bd9Sstevel@tonic-gate 	char		**c;
495*7c478bd9Sstevel@tonic-gate 	struct in_addr	in;
496*7c478bd9Sstevel@tonic-gate 
497*7c478bd9Sstevel@tonic-gate 	h = gethostbyname_r(host, (void *)&res, buf, sizeof (buf), &error);
498*7c478bd9Sstevel@tonic-gate 	if (h == NULL)
499*7c478bd9Sstevel@tonic-gate 		return (FALSE);
500*7c478bd9Sstevel@tonic-gate 	if (h->h_addrtype != AF_INET)
501*7c478bd9Sstevel@tonic-gate 		return (FALSE);
502*7c478bd9Sstevel@tonic-gate 	for (c = h->h_addr_list; *c != NULL; c++) {
503*7c478bd9Sstevel@tonic-gate 		(void) memcpy((char *)&in.s_addr, *c, sizeof (in.s_addr));
504*7c478bd9Sstevel@tonic-gate 		if (is_my_address(in))
505*7c478bd9Sstevel@tonic-gate 			return (TRUE);
506*7c478bd9Sstevel@tonic-gate 	}
507*7c478bd9Sstevel@tonic-gate 	return (FALSE);
508*7c478bd9Sstevel@tonic-gate }
509*7c478bd9Sstevel@tonic-gate 
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate /*
512*7c478bd9Sstevel@tonic-gate  * Given an IP address, find the interface address that has the best
513*7c478bd9Sstevel@tonic-gate  * prefix match.  Return the address in network order.
514*7c478bd9Sstevel@tonic-gate  */
515*7c478bd9Sstevel@tonic-gate static uint32_t
516*7c478bd9Sstevel@tonic-gate get_best_match(addr)
517*7c478bd9Sstevel@tonic-gate 	struct in_addr addr;
518*7c478bd9Sstevel@tonic-gate {
519*7c478bd9Sstevel@tonic-gate 	if_info_t *bestmatch, *ifn;
520*7c478bd9Sstevel@tonic-gate 	int bestcount, count, limit;
521*7c478bd9Sstevel@tonic-gate 	uint32_t mask, netmask, clnt_addr, if_addr;
522*7c478bd9Sstevel@tonic-gate 	bool_t found, subnet_match;
523*7c478bd9Sstevel@tonic-gate 	int subnet_count;
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	bestmatch = NULL;				/* no match yet */
526*7c478bd9Sstevel@tonic-gate 	bestcount = BITSPERBYTE * sizeof (uint32_t);	/* worst match */
527*7c478bd9Sstevel@tonic-gate 	clnt_addr = ntohl(addr.s_addr);			/* host order */
528*7c478bd9Sstevel@tonic-gate 
529*7c478bd9Sstevel@tonic-gate 	subnet_match = FALSE;		/* subnet match not found yet */
530*7c478bd9Sstevel@tonic-gate 	subnet_count = bestcount;	/* worst subnet match */
531*7c478bd9Sstevel@tonic-gate 
532*7c478bd9Sstevel@tonic-gate 	for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
533*7c478bd9Sstevel@tonic-gate 		netmask = ntohl(ifn->if_netmask.s_addr);  /* host order */
534*7c478bd9Sstevel@tonic-gate 		if_addr = ntohl(ifn->if_address.s_addr);  /* host order */
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 		/*
537*7c478bd9Sstevel@tonic-gate 		 * Checking if the interface selected is FAILED or DEPRECATED.
538*7c478bd9Sstevel@tonic-gate 		 * In case IFF_FAILED or IFF_DEPRECATED flag for the interface
539*7c478bd9Sstevel@tonic-gate 		 * is set, we move on to the next interface in the list.
540*7c478bd9Sstevel@tonic-gate 		 * Refer IPMP(IP Multi Pathing) for more details.
541*7c478bd9Sstevel@tonic-gate 		 */
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 		if ((ifn->if_flags & (IFF_FAILED | IFF_DEPRECATED)) != 0)
544*7c478bd9Sstevel@tonic-gate 			continue;
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 		/*
547*7c478bd9Sstevel@tonic-gate 		 * set initial count to first bit set in netmask, with
548*7c478bd9Sstevel@tonic-gate 		 * zero being the number of the least significant bit.
549*7c478bd9Sstevel@tonic-gate 		 */
550*7c478bd9Sstevel@tonic-gate 		for (count = 0, mask = netmask; mask && ((mask & 1) == 0);
551*7c478bd9Sstevel@tonic-gate 						count++, mask >>= 1);
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate 		/*
554*7c478bd9Sstevel@tonic-gate 		 * Set limit so that we don't try to match prefixes shorter
555*7c478bd9Sstevel@tonic-gate 		 * than the inherent netmask for the class (A, B, C, etc).
556*7c478bd9Sstevel@tonic-gate 		 */
557*7c478bd9Sstevel@tonic-gate 		if (IN_CLASSC(if_addr))
558*7c478bd9Sstevel@tonic-gate 			limit = IN_CLASSC_NSHIFT;
559*7c478bd9Sstevel@tonic-gate 		else if (IN_CLASSB(if_addr))
560*7c478bd9Sstevel@tonic-gate 			limit = IN_CLASSB_NSHIFT;
561*7c478bd9Sstevel@tonic-gate 		else if (IN_CLASSA(if_addr))
562*7c478bd9Sstevel@tonic-gate 			limit = IN_CLASSA_NSHIFT;
563*7c478bd9Sstevel@tonic-gate 		else
564*7c478bd9Sstevel@tonic-gate 			limit = 0;
565*7c478bd9Sstevel@tonic-gate 
566*7c478bd9Sstevel@tonic-gate 		/*
567*7c478bd9Sstevel@tonic-gate 		 * We assume that the netmask consists of a contiguous
568*7c478bd9Sstevel@tonic-gate 		 * sequence of 1-bits starting with the most significant bit.
569*7c478bd9Sstevel@tonic-gate 		 * Prefix comparison starts at the subnet mask level.
570*7c478bd9Sstevel@tonic-gate 		 * The prefix mask used for comparison is progressively
571*7c478bd9Sstevel@tonic-gate 		 * reduced until it equals the inherent mask for the
572*7c478bd9Sstevel@tonic-gate 		 * interface address class.  The algorithm finds an
573*7c478bd9Sstevel@tonic-gate 		 * interface in the following order of preference:
574*7c478bd9Sstevel@tonic-gate 		 *
575*7c478bd9Sstevel@tonic-gate 		 * (1) the longest subnet match
576*7c478bd9Sstevel@tonic-gate 		 * (2) the best partial subnet match
577*7c478bd9Sstevel@tonic-gate 		 * (3) the first non-loopback && non-PPP interface
578*7c478bd9Sstevel@tonic-gate 		 * (4) the first non-loopback interface (PPP is OK)
579*7c478bd9Sstevel@tonic-gate 		 *
580*7c478bd9Sstevel@tonic-gate 		 * While checking for condition (3) and (4), we also look
581*7c478bd9Sstevel@tonic-gate 		 * if the interface we are returning is neither FAILED
582*7c478bd9Sstevel@tonic-gate 		 * nor DEPRECATED. In case there are no interface
583*7c478bd9Sstevel@tonic-gate 		 * available, which are neither FAILED nor DEPRECRATED,
584*7c478bd9Sstevel@tonic-gate 		 * we return 0.
585*7c478bd9Sstevel@tonic-gate 		 */
586*7c478bd9Sstevel@tonic-gate 		found = FALSE;
587*7c478bd9Sstevel@tonic-gate 		while (netmask && count < subnet_count) {
588*7c478bd9Sstevel@tonic-gate 			if ((netmask & clnt_addr) == (netmask & if_addr)) {
589*7c478bd9Sstevel@tonic-gate 				bestcount = count;
590*7c478bd9Sstevel@tonic-gate 				bestmatch = ifn;
591*7c478bd9Sstevel@tonic-gate 				found = TRUE;
592*7c478bd9Sstevel@tonic-gate 				break;
593*7c478bd9Sstevel@tonic-gate 			}
594*7c478bd9Sstevel@tonic-gate 			netmask <<= 1;
595*7c478bd9Sstevel@tonic-gate 			count++;
596*7c478bd9Sstevel@tonic-gate 			if (count >= bestcount || count > limit || subnet_match)
597*7c478bd9Sstevel@tonic-gate 				break;
598*7c478bd9Sstevel@tonic-gate 		}
599*7c478bd9Sstevel@tonic-gate 		/*
600*7c478bd9Sstevel@tonic-gate 		 * If a subnet level match occurred, note this for
601*7c478bd9Sstevel@tonic-gate 		 * comparison with future subnet matches.
602*7c478bd9Sstevel@tonic-gate 		 */
603*7c478bd9Sstevel@tonic-gate 		if (found && (netmask == ntohl(ifn->if_netmask.s_addr))) {
604*7c478bd9Sstevel@tonic-gate 			subnet_match = TRUE;
605*7c478bd9Sstevel@tonic-gate 			subnet_count = count;
606*7c478bd9Sstevel@tonic-gate 		}
607*7c478bd9Sstevel@tonic-gate 	}
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate 	/*
610*7c478bd9Sstevel@tonic-gate 	 * If we don't have a match, select the first interface that
611*7c478bd9Sstevel@tonic-gate 	 * is not a loopback interface (and preferably not a PPP interface)
612*7c478bd9Sstevel@tonic-gate 	 * as the best match.
613*7c478bd9Sstevel@tonic-gate 	 */
614*7c478bd9Sstevel@tonic-gate 	if (bestmatch == NULL) {
615*7c478bd9Sstevel@tonic-gate 		for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
616*7c478bd9Sstevel@tonic-gate 			if ((ifn->if_flags & (IFF_LOOPBACK |
617*7c478bd9Sstevel@tonic-gate 				IFF_FAILED | IFF_DEPRECATED)) == 0) {
618*7c478bd9Sstevel@tonic-gate 				bestmatch = ifn;
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 				/*
621*7c478bd9Sstevel@tonic-gate 				 * If this isn't a PPP interface, we're
622*7c478bd9Sstevel@tonic-gate 				 * done.  Otherwise, keep walking through
623*7c478bd9Sstevel@tonic-gate 				 * the list in case we have a non-loopback
624*7c478bd9Sstevel@tonic-gate 				 * iface that ISN'T a PPP further down our
625*7c478bd9Sstevel@tonic-gate 				 * list...
626*7c478bd9Sstevel@tonic-gate 				 */
627*7c478bd9Sstevel@tonic-gate 				if ((ifn->if_flags & IFF_POINTOPOINT) == 0) {
628*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
629*7c478bd9Sstevel@tonic-gate 		(void) printf("found !loopback && !non-PPP interface: %s\n",
630*7c478bd9Sstevel@tonic-gate 				inet_ntoa(ifn->if_address));
631*7c478bd9Sstevel@tonic-gate #endif
632*7c478bd9Sstevel@tonic-gate 					break;
633*7c478bd9Sstevel@tonic-gate 				}
634*7c478bd9Sstevel@tonic-gate 			}
635*7c478bd9Sstevel@tonic-gate 		}
636*7c478bd9Sstevel@tonic-gate 	}
637*7c478bd9Sstevel@tonic-gate 
638*7c478bd9Sstevel@tonic-gate 	if (bestmatch != NULL)
639*7c478bd9Sstevel@tonic-gate 		return (bestmatch->if_address.s_addr);
640*7c478bd9Sstevel@tonic-gate 	else
641*7c478bd9Sstevel@tonic-gate 		return (0);
642*7c478bd9Sstevel@tonic-gate }
643*7c478bd9Sstevel@tonic-gate 
644*7c478bd9Sstevel@tonic-gate static int
645*7c478bd9Sstevel@tonic-gate is_myself(struct sockaddr_in6 *sa6)
646*7c478bd9Sstevel@tonic-gate {
647*7c478bd9Sstevel@tonic-gate 	struct sioc_addrreq areq;
648*7c478bd9Sstevel@tonic-gate 	int s;
649*7c478bd9Sstevel@tonic-gate 
650*7c478bd9Sstevel@tonic-gate 	if ((s = open("/dev/udp6", O_RDONLY)) < 0) {
651*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "is_myself: can't open /dev/udp6: %m");
652*7c478bd9Sstevel@tonic-gate 		return (0);
653*7c478bd9Sstevel@tonic-gate 	}
654*7c478bd9Sstevel@tonic-gate 
655*7c478bd9Sstevel@tonic-gate 	memcpy(&areq.sa_addr, (struct sockaddr_storage *)sa6,
656*7c478bd9Sstevel@tonic-gate 		sizeof (struct sockaddr_storage));
657*7c478bd9Sstevel@tonic-gate 	areq.sa_res = -1;
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate 	if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) {
660*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "is_myself:SIOCTMYADDR failed: %m");
661*7c478bd9Sstevel@tonic-gate 		close(s);
662*7c478bd9Sstevel@tonic-gate 		return (0);
663*7c478bd9Sstevel@tonic-gate 	}
664*7c478bd9Sstevel@tonic-gate 
665*7c478bd9Sstevel@tonic-gate 	close(s);
666*7c478bd9Sstevel@tonic-gate 	return (areq.sa_res);
667*7c478bd9Sstevel@tonic-gate 
668*7c478bd9Sstevel@tonic-gate }
669*7c478bd9Sstevel@tonic-gate /*
670*7c478bd9Sstevel@tonic-gate  * For a given destination address, determine a source address to use.
671*7c478bd9Sstevel@tonic-gate  * Returns wildcard address if it cannot determine the source address.
672*7c478bd9Sstevel@tonic-gate  * copied from ping.c.
673*7c478bd9Sstevel@tonic-gate  */
674*7c478bd9Sstevel@tonic-gate union any_in_addr {
675*7c478bd9Sstevel@tonic-gate 	struct in6_addr addr6;
676*7c478bd9Sstevel@tonic-gate 	struct in_addr addr;
677*7c478bd9Sstevel@tonic-gate };
678*7c478bd9Sstevel@tonic-gate static bool_t
679*7c478bd9Sstevel@tonic-gate select_server_addr(union any_in_addr *dst_addr, int family,
680*7c478bd9Sstevel@tonic-gate     union any_in_addr *src_addr)
681*7c478bd9Sstevel@tonic-gate {
682*7c478bd9Sstevel@tonic-gate 	struct sockaddr *sock;
683*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
684*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
685*7c478bd9Sstevel@tonic-gate 	int tmp_fd;
686*7c478bd9Sstevel@tonic-gate 	size_t sock_len;
687*7c478bd9Sstevel@tonic-gate 
688*7c478bd9Sstevel@tonic-gate 	sock = calloc(1, sizeof (struct sockaddr_in6));
689*7c478bd9Sstevel@tonic-gate 	if (sock == NULL) {
690*7c478bd9Sstevel@tonic-gate 		return (FALSE);
691*7c478bd9Sstevel@tonic-gate 	}
692*7c478bd9Sstevel@tonic-gate 
693*7c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
694*7c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)sock;
695*7c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
696*7c478bd9Sstevel@tonic-gate 		sin->sin_port = 111;
697*7c478bd9Sstevel@tonic-gate 		sin->sin_addr = dst_addr->addr;
698*7c478bd9Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in);
699*7c478bd9Sstevel@tonic-gate 	} else {
700*7c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)sock;
701*7c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
702*7c478bd9Sstevel@tonic-gate 		sin6->sin6_port = 111;
703*7c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = dst_addr->addr6;
704*7c478bd9Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in6);
705*7c478bd9Sstevel@tonic-gate 	}
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate 	/* open a UDP socket */
708*7c478bd9Sstevel@tonic-gate 	if ((tmp_fd = _so_socket(family, SOCK_DGRAM, 0,
709*7c478bd9Sstevel@tonic-gate 		NULL, SOV_SOCKBSD)) < 0) {
710*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "selsect_server_addr:connect failed\n");
711*7c478bd9Sstevel@tonic-gate 		return (FALSE);
712*7c478bd9Sstevel@tonic-gate 	}
713*7c478bd9Sstevel@tonic-gate 
714*7c478bd9Sstevel@tonic-gate 	/* connect it */
715*7c478bd9Sstevel@tonic-gate 	if (_so_connect(tmp_fd, sock, sock_len, SOV_SOCKBSD) < 0) {
716*7c478bd9Sstevel@tonic-gate 		/*
717*7c478bd9Sstevel@tonic-gate 		 * If there's no route to the destination, this connect() call
718*7c478bd9Sstevel@tonic-gate 		 * fails. We just return all-zero (wildcard) as the source
719*7c478bd9Sstevel@tonic-gate 		 * address, so that user can get to see "no route to dest"
720*7c478bd9Sstevel@tonic-gate 		 * message, as it'll try to send the probe packet out and will
721*7c478bd9Sstevel@tonic-gate 		 * receive ICMP unreachable.
722*7c478bd9Sstevel@tonic-gate 		 */
723*7c478bd9Sstevel@tonic-gate 		if (family == AF_INET)
724*7c478bd9Sstevel@tonic-gate 			src_addr->addr.s_addr = INADDR_ANY;
725*7c478bd9Sstevel@tonic-gate 		else
726*7c478bd9Sstevel@tonic-gate 			/*
727*7c478bd9Sstevel@tonic-gate 			 * Since in6addr_any is not in the scope
728*7c478bd9Sstevel@tonic-gate 			 * use the following hack
729*7c478bd9Sstevel@tonic-gate 			 */
730*7c478bd9Sstevel@tonic-gate 			memset(src_addr->addr6.s6_addr,
731*7c478bd9Sstevel@tonic-gate 				0, sizeof (struct in6_addr));
732*7c478bd9Sstevel@tonic-gate 		(void) close(tmp_fd);
733*7c478bd9Sstevel@tonic-gate 		free(sock);
734*7c478bd9Sstevel@tonic-gate 		return (FALSE);
735*7c478bd9Sstevel@tonic-gate 	}
736*7c478bd9Sstevel@tonic-gate 
737*7c478bd9Sstevel@tonic-gate 	/* get the local sock info */
738*7c478bd9Sstevel@tonic-gate 	if (_so_getsockname(tmp_fd, sock, &sock_len, SOV_DEFAULT) < 0) {
739*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "selsect_server_addr:getsockname failed\n");
740*7c478bd9Sstevel@tonic-gate 		(void) close(tmp_fd);
741*7c478bd9Sstevel@tonic-gate 		free(sock);
742*7c478bd9Sstevel@tonic-gate 		return (FALSE);
743*7c478bd9Sstevel@tonic-gate 	}
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
746*7c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)sock;
747*7c478bd9Sstevel@tonic-gate 		src_addr->addr = sin->sin_addr;
748*7c478bd9Sstevel@tonic-gate 	} else {
749*7c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)sock;
750*7c478bd9Sstevel@tonic-gate 		src_addr->addr6 = sin6->sin6_addr;
751*7c478bd9Sstevel@tonic-gate 	}
752*7c478bd9Sstevel@tonic-gate 
753*7c478bd9Sstevel@tonic-gate 	(void) close(tmp_fd);
754*7c478bd9Sstevel@tonic-gate 	free(sock);
755*7c478bd9Sstevel@tonic-gate 	return (TRUE);
756*7c478bd9Sstevel@tonic-gate }
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate /*
759*7c478bd9Sstevel@tonic-gate  * This internal routine will merge one of those "universal" addresses
760*7c478bd9Sstevel@tonic-gate  * to the one which will make sense to the remote caller.
761*7c478bd9Sstevel@tonic-gate  */
762*7c478bd9Sstevel@tonic-gate static char *
763*7c478bd9Sstevel@tonic-gate inet_netdir_mergeaddr(tp, ruaddr, uaddr)
764*7c478bd9Sstevel@tonic-gate 	struct netconfig	*tp;	/* the transport provider */
765*7c478bd9Sstevel@tonic-gate 	char			*ruaddr; /* remote uaddr of the caller */
766*7c478bd9Sstevel@tonic-gate 	char			*uaddr;	/* the address */
767*7c478bd9Sstevel@tonic-gate {
768*7c478bd9Sstevel@tonic-gate 	char	tmp[SYS_NMLN], *cp;
769*7c478bd9Sstevel@tonic-gate 	int	j;
770*7c478bd9Sstevel@tonic-gate 	struct	in_addr clientaddr, bestmatch;
771*7c478bd9Sstevel@tonic-gate 	time_t	curtime;
772*7c478bd9Sstevel@tonic-gate 	int af;
773*7c478bd9Sstevel@tonic-gate 
774*7c478bd9Sstevel@tonic-gate 	if (!uaddr || !ruaddr || !tp) {
775*7c478bd9Sstevel@tonic-gate 		_nderror = ND_BADARG;
776*7c478bd9Sstevel@tonic-gate 		return (NULL);
777*7c478bd9Sstevel@tonic-gate 	}
778*7c478bd9Sstevel@tonic-gate 	(void) bzero(tmp, SYS_NMLN);
779*7c478bd9Sstevel@tonic-gate 
780*7c478bd9Sstevel@tonic-gate 	if (strcmp(tp->nc_protofmly, NC_INET) == 0)
781*7c478bd9Sstevel@tonic-gate 		af = AF_INET;
782*7c478bd9Sstevel@tonic-gate 	else
783*7c478bd9Sstevel@tonic-gate 		af = AF_INET6;
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate 	if (af == AF_INET) {
786*7c478bd9Sstevel@tonic-gate 		if (strncmp(ruaddr, "0.0.0.0.", strlen("0.0.0.0.")) == 0)
787*7c478bd9Sstevel@tonic-gate 			/* thats me: return the way it is */
788*7c478bd9Sstevel@tonic-gate 			return (strdup(uaddr));
789*7c478bd9Sstevel@tonic-gate 
790*7c478bd9Sstevel@tonic-gate 		/*
791*7c478bd9Sstevel@tonic-gate 		 * Convert remote uaddr into an in_addr so that we can compare
792*7c478bd9Sstevel@tonic-gate 		 * to it.  Shave off last two dotted-decimal values.
793*7c478bd9Sstevel@tonic-gate 		 */
794*7c478bd9Sstevel@tonic-gate 		for (cp = ruaddr, j = 0; j < 4; j++, cp++)
795*7c478bd9Sstevel@tonic-gate 			if ((cp = strchr(cp, '.')) == NULL)
796*7c478bd9Sstevel@tonic-gate 				break;
797*7c478bd9Sstevel@tonic-gate 
798*7c478bd9Sstevel@tonic-gate 		if (cp != NULL)
799*7c478bd9Sstevel@tonic-gate 			*--cp = '\0';	/* null out the dot after the IP addr */
800*7c478bd9Sstevel@tonic-gate 		else {
801*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOHOST;
802*7c478bd9Sstevel@tonic-gate 			return (NULL);
803*7c478bd9Sstevel@tonic-gate 		}
804*7c478bd9Sstevel@tonic-gate 
805*7c478bd9Sstevel@tonic-gate 		clientaddr.s_addr = inet_addr(ruaddr);
806*7c478bd9Sstevel@tonic-gate 
807*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
808*7c478bd9Sstevel@tonic-gate 		(void) printf("client's address is %s and %s\n",
809*7c478bd9Sstevel@tonic-gate 			ruaddr, inet_ntoa(clientaddr));
810*7c478bd9Sstevel@tonic-gate #endif
811*7c478bd9Sstevel@tonic-gate 
812*7c478bd9Sstevel@tonic-gate 		/* We know cp is not NULL due to the check above */
813*7c478bd9Sstevel@tonic-gate 		*cp = '.';	/* Put the dot back in the IP addr */
814*7c478bd9Sstevel@tonic-gate 
815*7c478bd9Sstevel@tonic-gate 		(void) time(&curtime);
816*7c478bd9Sstevel@tonic-gate 		if ((curtime - last_updated) >= if_cache_refresh_time) {
817*7c478bd9Sstevel@tonic-gate 			/*
818*7c478bd9Sstevel@tonic-gate 			 * Cache needs to be refreshed.
819*7c478bd9Sstevel@tonic-gate 			 */
820*7c478bd9Sstevel@tonic-gate 			if (!update_if_cache())
821*7c478bd9Sstevel@tonic-gate 				return (NULL);
822*7c478bd9Sstevel@tonic-gate 		}
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate 		/*
825*7c478bd9Sstevel@tonic-gate 		 * Find the best match now.
826*7c478bd9Sstevel@tonic-gate 		 */
827*7c478bd9Sstevel@tonic-gate 		(void) rw_rdlock(&iflock);
828*7c478bd9Sstevel@tonic-gate 		bestmatch.s_addr = get_best_match(clientaddr);
829*7c478bd9Sstevel@tonic-gate 		(void) rw_unlock(&iflock);
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 		if (bestmatch.s_addr)
832*7c478bd9Sstevel@tonic-gate 			_nderror = ND_OK;
833*7c478bd9Sstevel@tonic-gate 		else {
834*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOHOST;
835*7c478bd9Sstevel@tonic-gate 			return (NULL);
836*7c478bd9Sstevel@tonic-gate 		}
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 		/* prepare the reply */
839*7c478bd9Sstevel@tonic-gate 		(void) memset(tmp, '\0', sizeof (tmp));
840*7c478bd9Sstevel@tonic-gate 
841*7c478bd9Sstevel@tonic-gate 		/* reply consists of the IP addr of the closest interface */
842*7c478bd9Sstevel@tonic-gate 		(void) strcpy(tmp, inet_ntoa(bestmatch));
843*7c478bd9Sstevel@tonic-gate 
844*7c478bd9Sstevel@tonic-gate 		/*
845*7c478bd9Sstevel@tonic-gate 		 * ... and the port number part (last two dotted-decimal values)
846*7c478bd9Sstevel@tonic-gate 		 * of uaddr
847*7c478bd9Sstevel@tonic-gate 		 */
848*7c478bd9Sstevel@tonic-gate 		for (cp = uaddr, j = 0; j < 4; j++, cp++)
849*7c478bd9Sstevel@tonic-gate 			cp = strchr(cp, '.');
850*7c478bd9Sstevel@tonic-gate 		(void) strcat(tmp, --cp);
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate 	} else {
853*7c478bd9Sstevel@tonic-gate 		/* IPv6 */
854*7c478bd9Sstevel@tonic-gate 		char *dot;
855*7c478bd9Sstevel@tonic-gate 		char *truaddr;
856*7c478bd9Sstevel@tonic-gate 		char  name2[SYS_NMLN];
857*7c478bd9Sstevel@tonic-gate 		struct sockaddr_in6 sa;
858*7c478bd9Sstevel@tonic-gate 		struct sockaddr_in6 server_addr;
859*7c478bd9Sstevel@tonic-gate 		union any_in_addr in_addr, out_addr;
860*7c478bd9Sstevel@tonic-gate 
861*7c478bd9Sstevel@tonic-gate 		if (strncmp(ruaddr, "::", strlen("::")) == 0)
862*7c478bd9Sstevel@tonic-gate 			if (*(ruaddr + strlen("::")) == '\0')
863*7c478bd9Sstevel@tonic-gate 				/* thats me: return the way it is */
864*7c478bd9Sstevel@tonic-gate 				return (strdup(uaddr));
865*7c478bd9Sstevel@tonic-gate 
866*7c478bd9Sstevel@tonic-gate 		bzero(&sa, sizeof (sa));
867*7c478bd9Sstevel@tonic-gate 		bzero(&server_addr, sizeof (server_addr));
868*7c478bd9Sstevel@tonic-gate 		truaddr = &tmp[0];
869*7c478bd9Sstevel@tonic-gate 		strcpy(truaddr, ruaddr);
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate 		/*
872*7c478bd9Sstevel@tonic-gate 		 * now extract the server ip address from
873*7c478bd9Sstevel@tonic-gate 		 * the address supplied by client.  It can be
874*7c478bd9Sstevel@tonic-gate 		 * client's own IP address.
875*7c478bd9Sstevel@tonic-gate 		 */
876*7c478bd9Sstevel@tonic-gate 
877*7c478bd9Sstevel@tonic-gate 		if ((dot = strrchr(truaddr, '.')) != 0) {
878*7c478bd9Sstevel@tonic-gate 			*dot = '\0';
879*7c478bd9Sstevel@tonic-gate 			if ((dot = strrchr(truaddr, '.')) != 0)
880*7c478bd9Sstevel@tonic-gate 				*dot = '\0';
881*7c478bd9Sstevel@tonic-gate 		}
882*7c478bd9Sstevel@tonic-gate 
883*7c478bd9Sstevel@tonic-gate 		if (dot == 0) {
884*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOHOST;
885*7c478bd9Sstevel@tonic-gate 			return (NULL);
886*7c478bd9Sstevel@tonic-gate 		}
887*7c478bd9Sstevel@tonic-gate 
888*7c478bd9Sstevel@tonic-gate 		if (inet_pton(af, truaddr, sa.sin6_addr.s6_addr)
889*7c478bd9Sstevel@tonic-gate 		    != 1) {
890*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOHOST;
891*7c478bd9Sstevel@tonic-gate 			return (NULL);
892*7c478bd9Sstevel@tonic-gate 		}
893*7c478bd9Sstevel@tonic-gate 
894*7c478bd9Sstevel@tonic-gate 		in_addr.addr6 = sa.sin6_addr;
895*7c478bd9Sstevel@tonic-gate 		sa.sin6_family = AF_INET6;
896*7c478bd9Sstevel@tonic-gate 
897*7c478bd9Sstevel@tonic-gate 		/* is it my IP address */
898*7c478bd9Sstevel@tonic-gate 		if (!is_myself(&sa)) {
899*7c478bd9Sstevel@tonic-gate 			/* have the kernel select one for me */
900*7c478bd9Sstevel@tonic-gate 			if (select_server_addr(&in_addr, af, &out_addr) ==
901*7c478bd9Sstevel@tonic-gate 			    FALSE)
902*7c478bd9Sstevel@tonic-gate 				return (NULL);
903*7c478bd9Sstevel@tonic-gate 			server_addr.sin6_addr = out_addr.addr6;
904*7c478bd9Sstevel@tonic-gate 		}
905*7c478bd9Sstevel@tonic-gate 		else
906*7c478bd9Sstevel@tonic-gate 			memcpy((char *)&server_addr, (char *)&sa,
907*7c478bd9Sstevel@tonic-gate 				sizeof (struct sockaddr_in6));
908*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
909*7c478bd9Sstevel@tonic-gate 		printf("%s\n", inet_ntop(af, out_addr.addr6.s6_addr,
910*7c478bd9Sstevel@tonic-gate 			tmp, sizeof (tmp)));
911*7c478bd9Sstevel@tonic-gate #endif
912*7c478bd9Sstevel@tonic-gate 
913*7c478bd9Sstevel@tonic-gate 		if (inet_ntop(af, server_addr.sin6_addr.s6_addr,
914*7c478bd9Sstevel@tonic-gate 			tmp, sizeof (tmp)) == NULL) {
915*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOHOST;
916*7c478bd9Sstevel@tonic-gate 			return (NULL);
917*7c478bd9Sstevel@tonic-gate 		}
918*7c478bd9Sstevel@tonic-gate 
919*7c478bd9Sstevel@tonic-gate 		/* now extract the port info */
920*7c478bd9Sstevel@tonic-gate 		if ((dot = strrchr(uaddr, '.')) != 0) {
921*7c478bd9Sstevel@tonic-gate 
922*7c478bd9Sstevel@tonic-gate 			char *p;
923*7c478bd9Sstevel@tonic-gate 
924*7c478bd9Sstevel@tonic-gate 			p = --dot;
925*7c478bd9Sstevel@tonic-gate 			while (*p-- != '.');
926*7c478bd9Sstevel@tonic-gate 			p++;
927*7c478bd9Sstevel@tonic-gate 			strcat(tmp + strlen(tmp), p);
928*7c478bd9Sstevel@tonic-gate 			_nderror = ND_OK;
929*7c478bd9Sstevel@tonic-gate 		} else {
930*7c478bd9Sstevel@tonic-gate 			_nderror = ND_NOHOST;
931*7c478bd9Sstevel@tonic-gate 			return (NULL);
932*7c478bd9Sstevel@tonic-gate 		}
933*7c478bd9Sstevel@tonic-gate 
934*7c478bd9Sstevel@tonic-gate 	}
935*7c478bd9Sstevel@tonic-gate 	return (strdup(tmp));
936*7c478bd9Sstevel@tonic-gate }
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate static int
939*7c478bd9Sstevel@tonic-gate bindresvport(nconf, fd, addr)
940*7c478bd9Sstevel@tonic-gate 	struct netconfig *nconf;
941*7c478bd9Sstevel@tonic-gate 	int fd;
942*7c478bd9Sstevel@tonic-gate 	struct netbuf *addr;
943*7c478bd9Sstevel@tonic-gate {
944*7c478bd9Sstevel@tonic-gate 	int res;
945*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in myaddr;
946*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 myaddr6;
947*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
948*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
949*7c478bd9Sstevel@tonic-gate 	int i;
950*7c478bd9Sstevel@tonic-gate 	struct t_bind tbindstr, *tres;
951*7c478bd9Sstevel@tonic-gate 	struct t_info tinfo;
952*7c478bd9Sstevel@tonic-gate 	struct t_optmgmt req, resp;
953*7c478bd9Sstevel@tonic-gate 	struct opthdr *opt;
954*7c478bd9Sstevel@tonic-gate 	int reqbuf[64/sizeof (int)];
955*7c478bd9Sstevel@tonic-gate 	int *optval;
956*7c478bd9Sstevel@tonic-gate 
957*7c478bd9Sstevel@tonic-gate 	union {
958*7c478bd9Sstevel@tonic-gate 		struct sockaddr_in *sin;
959*7c478bd9Sstevel@tonic-gate 		struct sockaddr_in6 *sin6;
960*7c478bd9Sstevel@tonic-gate 		char *buf;
961*7c478bd9Sstevel@tonic-gate 	} u;
962*7c478bd9Sstevel@tonic-gate 
963*7c478bd9Sstevel@tonic-gate 	_nderror = ND_SYSTEM;
964*7c478bd9Sstevel@tonic-gate 	if (geteuid()) {
965*7c478bd9Sstevel@tonic-gate 		errno = EACCES;
966*7c478bd9Sstevel@tonic-gate 		return (-1);
967*7c478bd9Sstevel@tonic-gate 	}
968*7c478bd9Sstevel@tonic-gate 	if ((i = t_getstate(fd)) != T_UNBND) {
969*7c478bd9Sstevel@tonic-gate 		if (t_errno == TBADF)
970*7c478bd9Sstevel@tonic-gate 			errno = EBADF;
971*7c478bd9Sstevel@tonic-gate 		if (i != -1)
972*7c478bd9Sstevel@tonic-gate 			errno = EISCONN;
973*7c478bd9Sstevel@tonic-gate 		return (-1);
974*7c478bd9Sstevel@tonic-gate 	}
975*7c478bd9Sstevel@tonic-gate 
976*7c478bd9Sstevel@tonic-gate 	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
977*7c478bd9Sstevel@tonic-gate 		if (addr == NULL) {
978*7c478bd9Sstevel@tonic-gate 			sin = &myaddr;
979*7c478bd9Sstevel@tonic-gate 			(void) memset((char *)sin, 0, sizeof (*sin));
980*7c478bd9Sstevel@tonic-gate 			sin->sin_family = AF_INET;
981*7c478bd9Sstevel@tonic-gate 			u.buf = (char *)sin;
982*7c478bd9Sstevel@tonic-gate 		} else
983*7c478bd9Sstevel@tonic-gate 			u.buf = (char *)addr->buf;
984*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
985*7c478bd9Sstevel@tonic-gate 		if (addr == NULL) {
986*7c478bd9Sstevel@tonic-gate 			sin6 = &myaddr6;
987*7c478bd9Sstevel@tonic-gate 			(void) memset((char *)sin6, 0, sizeof (*sin6));
988*7c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
989*7c478bd9Sstevel@tonic-gate 			u.buf = (char *)sin6;
990*7c478bd9Sstevel@tonic-gate 		} else
991*7c478bd9Sstevel@tonic-gate 			u.buf = addr->buf;
992*7c478bd9Sstevel@tonic-gate 
993*7c478bd9Sstevel@tonic-gate 	} else {
994*7c478bd9Sstevel@tonic-gate 		errno = EPFNOSUPPORT;
995*7c478bd9Sstevel@tonic-gate 		return (-1);
996*7c478bd9Sstevel@tonic-gate 	}
997*7c478bd9Sstevel@tonic-gate 
998*7c478bd9Sstevel@tonic-gate 	/* Transform sockaddr_in to netbuf */
999*7c478bd9Sstevel@tonic-gate 	if (t_getinfo(fd, &tinfo) == -1)
1000*7c478bd9Sstevel@tonic-gate 		return (-1);
1001*7c478bd9Sstevel@tonic-gate 	tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
1002*7c478bd9Sstevel@tonic-gate 	if (tres == NULL) {
1003*7c478bd9Sstevel@tonic-gate 		_nderror = ND_NOMEM;
1004*7c478bd9Sstevel@tonic-gate 		return (-1);
1005*7c478bd9Sstevel@tonic-gate 	}
1006*7c478bd9Sstevel@tonic-gate 
1007*7c478bd9Sstevel@tonic-gate 	tbindstr.qlen = 0; /* Always 0; user should change if he wants to */
1008*7c478bd9Sstevel@tonic-gate 	tbindstr.addr.buf = (char *)u.buf;
1009*7c478bd9Sstevel@tonic-gate 	tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
1010*7c478bd9Sstevel@tonic-gate 
1011*7c478bd9Sstevel@tonic-gate 	/*
1012*7c478bd9Sstevel@tonic-gate 	 * Use *_ANONPRIVBIND to ask the kernel to pick a port in the
1013*7c478bd9Sstevel@tonic-gate 	 * priviledged range for us.
1014*7c478bd9Sstevel@tonic-gate 	 */
1015*7c478bd9Sstevel@tonic-gate 	opt = (struct opthdr *)reqbuf;
1016*7c478bd9Sstevel@tonic-gate 	if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
1017*7c478bd9Sstevel@tonic-gate 		opt->level = IPPROTO_TCP;
1018*7c478bd9Sstevel@tonic-gate 		opt->name = TCP_ANONPRIVBIND;
1019*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
1020*7c478bd9Sstevel@tonic-gate 		opt->level = IPPROTO_UDP;
1021*7c478bd9Sstevel@tonic-gate 		opt->name = UDP_ANONPRIVBIND;
1022*7c478bd9Sstevel@tonic-gate 	} else {
1023*7c478bd9Sstevel@tonic-gate 		errno = EPROTONOSUPPORT;
1024*7c478bd9Sstevel@tonic-gate 		(void) t_free((char *)tres, T_BIND);
1025*7c478bd9Sstevel@tonic-gate 		return (-1);
1026*7c478bd9Sstevel@tonic-gate 	}
1027*7c478bd9Sstevel@tonic-gate 
1028*7c478bd9Sstevel@tonic-gate 	opt->len = sizeof (int);
1029*7c478bd9Sstevel@tonic-gate 	req.flags = T_NEGOTIATE;
1030*7c478bd9Sstevel@tonic-gate 	req.opt.len = sizeof (struct opthdr) + opt->len;
1031*7c478bd9Sstevel@tonic-gate 	req.opt.buf = (char *)opt;
1032*7c478bd9Sstevel@tonic-gate 	optval = (int *)((char *)reqbuf + sizeof (struct opthdr));
1033*7c478bd9Sstevel@tonic-gate 	*optval = 1;
1034*7c478bd9Sstevel@tonic-gate 	resp.flags = 0;
1035*7c478bd9Sstevel@tonic-gate 	resp.opt.buf = (char *)reqbuf;
1036*7c478bd9Sstevel@tonic-gate 	resp.opt.maxlen = sizeof (reqbuf);
1037*7c478bd9Sstevel@tonic-gate 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
1038*7c478bd9Sstevel@tonic-gate 		(void) t_free((char *)tres, T_BIND);
1039*7c478bd9Sstevel@tonic-gate 		return (-1);
1040*7c478bd9Sstevel@tonic-gate 	}
1041*7c478bd9Sstevel@tonic-gate 
1042*7c478bd9Sstevel@tonic-gate 	if (u.sin->sin_family == AF_INET)
1043*7c478bd9Sstevel@tonic-gate 		u.sin->sin_port = htons(0);
1044*7c478bd9Sstevel@tonic-gate 	else
1045*7c478bd9Sstevel@tonic-gate 		u.sin6->sin6_port = htons(0);
1046*7c478bd9Sstevel@tonic-gate 	res = t_bind(fd, &tbindstr, tres);
1047*7c478bd9Sstevel@tonic-gate 	if (res != 0) {
1048*7c478bd9Sstevel@tonic-gate 		if (t_errno == TNOADDR) {
1049*7c478bd9Sstevel@tonic-gate 			_nderror = ND_FAILCTRL;
1050*7c478bd9Sstevel@tonic-gate 			res = 1;
1051*7c478bd9Sstevel@tonic-gate 		}
1052*7c478bd9Sstevel@tonic-gate 	} else {
1053*7c478bd9Sstevel@tonic-gate 		_nderror = ND_OK;
1054*7c478bd9Sstevel@tonic-gate 	}
1055*7c478bd9Sstevel@tonic-gate 
1056*7c478bd9Sstevel@tonic-gate 	/*
1057*7c478bd9Sstevel@tonic-gate 	 * Always turn off the option when we are done.  Note that by doing
1058*7c478bd9Sstevel@tonic-gate 	 * this, if the caller has set this option before calling
1059*7c478bd9Sstevel@tonic-gate 	 * bindresvport(), it will be unset.  Better be safe...
1060*7c478bd9Sstevel@tonic-gate 	 */
1061*7c478bd9Sstevel@tonic-gate 	 *optval = 0;
1062*7c478bd9Sstevel@tonic-gate 	resp.flags = 0;
1063*7c478bd9Sstevel@tonic-gate 	resp.opt.buf = (char *)reqbuf;
1064*7c478bd9Sstevel@tonic-gate 	resp.opt.maxlen = sizeof (reqbuf);
1065*7c478bd9Sstevel@tonic-gate 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
1066*7c478bd9Sstevel@tonic-gate 		(void) t_free((char *)tres, T_BIND);
1067*7c478bd9Sstevel@tonic-gate 		if (res == 0)
1068*7c478bd9Sstevel@tonic-gate 			(void) t_unbind(fd);
1069*7c478bd9Sstevel@tonic-gate 		_nderror = ND_FAILCTRL;
1070*7c478bd9Sstevel@tonic-gate 		return (-1);
1071*7c478bd9Sstevel@tonic-gate 	}
1072*7c478bd9Sstevel@tonic-gate 
1073*7c478bd9Sstevel@tonic-gate 	(void) t_free((char *)tres, T_BIND);
1074*7c478bd9Sstevel@tonic-gate 	return (res);
1075*7c478bd9Sstevel@tonic-gate }
1076*7c478bd9Sstevel@tonic-gate 
1077*7c478bd9Sstevel@tonic-gate static int
1078*7c478bd9Sstevel@tonic-gate checkresvport(addr)
1079*7c478bd9Sstevel@tonic-gate 	struct netbuf *addr;
1080*7c478bd9Sstevel@tonic-gate {
1081*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
1082*7c478bd9Sstevel@tonic-gate 	unsigned short port;
1083*7c478bd9Sstevel@tonic-gate 
1084*7c478bd9Sstevel@tonic-gate 	if (addr == NULL) {
1085*7c478bd9Sstevel@tonic-gate 		_nderror = ND_FAILCTRL;
1086*7c478bd9Sstevel@tonic-gate 		return (-1);
1087*7c478bd9Sstevel@tonic-gate 	}
1088*7c478bd9Sstevel@tonic-gate 	/*
1089*7c478bd9Sstevel@tonic-gate 	 * Still works for IPv6 since the first two memebers of
1090*7c478bd9Sstevel@tonic-gate 	 * both address structure point to family and port # respectively
1091*7c478bd9Sstevel@tonic-gate 	 */
1092*7c478bd9Sstevel@tonic-gate 	sin = (struct sockaddr_in *)(addr->buf);
1093*7c478bd9Sstevel@tonic-gate 	port = ntohs(sin->sin_port);
1094*7c478bd9Sstevel@tonic-gate 	if (port < IPPORT_RESERVED)
1095*7c478bd9Sstevel@tonic-gate 		return (0);
1096*7c478bd9Sstevel@tonic-gate 	return (1);
1097*7c478bd9Sstevel@tonic-gate }
1098