xref: /titanic_52/usr/src/lib/libnsl/nss/netdir_inet.c (revision 82d33c01b078ed404a986a369750cdb4743773fb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This is where we have chosen to combine every useful bit of code for
29  * all the Solaris frontends to lookup hosts, services, and netdir information
30  * for inet family (udp, tcp) transports. gethostbyYY(), getservbyYY(), and
31  * netdir_getbyYY() are all implemented on top of this code. Similarly,
32  * netdir_options, taddr2uaddr, and uaddr2taddr for inet transports also
33  * find a home here.
34  *
35  * If the netconfig structure supplied has NO nametoaddr libs (i.e. a "-"
36  * in /etc/netconfig), this code calls the name service switch, and
37  * therefore, /etc/nsswitch.conf is effectively the only place that
38  * dictates hosts/serv lookup policy.
39  * If an administrator chooses to bypass the name service switch by
40  * specifying third party supplied nametoaddr libs in /etc/netconfig, this
41  * implementation does NOT call the name service switch, it merely loops
42  * through the nametoaddr libs. In this case, if this code was called
43  * from gethost/servbyYY() we marshal the inet specific struct into
44  * transport independent netbuf or hostserv, and unmarshal the resulting
45  * nd_addrlist or hostservlist back into hostent and servent, as the case
46  * may be.
47  *
48  * Goes without saying that most of the future bugs in gethost/servbyYY
49  * and netdir_getbyYY are lurking somewhere here.
50  */
51 
52 #pragma ident	"%Z%%M%	%I%	%E% SMI"
53 
54 #include "mt.h"
55 #include <ctype.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <stropts.h>
61 #include <sys/types.h>
62 #include <sys/byteorder.h>
63 #include <sys/ioctl.h>
64 #include <sys/param.h>
65 #include <sys/time.h>
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <thread.h>
69 #include <synch.h>
70 #include <sys/utsname.h>
71 #include <netdb.h>
72 #include <netconfig.h>
73 #include <netdir.h>
74 #include <tiuser.h>
75 #include <sys/socket.h>
76 #include <sys/sockio.h>
77 #include <netinet/in.h>
78 #include <arpa/inet.h>
79 #include <net/if.h>
80 #include <inet/ip.h>
81 #include <inet/ip6_asp.h>
82 #include <sys/dlpi.h>
83 #include <nss_dbdefs.h>
84 #include <nss_netdir.h>
85 #include <syslog.h>
86 #include <nsswitch.h>
87 #include "nss.h"
88 
89 #define	MAXIFS 32
90 #define	UDPDEV	"/dev/udp"
91 #define	UDP6DEV	"/dev/udp6"
92 
93 #ifdef PIC
94 #define	DOOR_GETHOSTBYNAME_R	_door_gethostbyname_r
95 #define	DOOR_GETHOSTBYADDR_R	_door_gethostbyaddr_r
96 #define	DOOR_GETIPNODEBYNAME_R	_door_getipnodebyname_r
97 #define	DOOR_GETIPNODEBYADDR_R	_door_getipnodebyaddr_r
98 #else
99 #define	DOOR_GETHOSTBYNAME_R	_switch_gethostbyname_r
100 #define	DOOR_GETHOSTBYADDR_R	_switch_gethostbyaddr_r
101 #define	DOOR_GETIPNODEBYNAME_R	_switch_getipnodebyname_r
102 #define	DOOR_GETIPNODEBYADDR_R	_switch_getipnodebyaddr_r
103 #endif /* PIC */
104 
105 #define	DONT_SORT	"SORT_ADDRS=NO"
106 #define	DONT_SORT2	"SORT_ADDRS=FALSE"
107 #define	LINESIZE	100
108 
109 /*
110  * constant values of addresses for HOST_SELF_BIND, HOST_SELF_CONNECT
111  * and localhost.
112  *
113  * The following variables are static to the extent that they should
114  * not be visible outside of this file.
115  */
116 static char *localaddr[] = {"\000\000\000\000", NULL};
117 static char *connectaddr[] = {"\177\000\000\001", NULL};
118 static char *localaddr6[] =
119 {"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", NULL};
120 static char *connectaddr6[] =
121 {"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", NULL};
122 
123 /* IPv4 nd_addrlist */
124 static mutex_t	nd_addr_lock = DEFAULTMUTEX;
125 static struct sockaddr_in sa_con;
126 static struct netbuf nd_conbuf = {sizeof (sa_con),\
127     sizeof (sa_con), (char *)&sa_con};
128 static struct nd_addrlist nd_conaddrlist = {1, &nd_conbuf};
129 
130 /* IPv6 nd_addrlist */
131 static mutex_t	nd6_addr_lock = DEFAULTMUTEX;
132 static struct sockaddr_in6 sa6_con;
133 static struct netbuf nd6_conbuf = {sizeof (sa6_con),\
134 	sizeof (sa6_con), (char *)&sa6_con};
135 static struct nd_addrlist nd6_conaddrlist = {1, &nd6_conbuf};
136 
137 #define	LOCALHOST "localhost"
138 
139 struct servent *_switch_getservbyname_r(const char *, const char *,
140     struct servent *, char *, int);
141 struct servent *_switch_getservbyport_r(int, const char *, struct servent *,
142     char *, int);
143 
144 static int __herrno2netdir(int h_errnop);
145 static struct ifinfo *get_local_info(void);
146 static int getbroadcastnets(struct netconfig *, struct in_addr **);
147 static int hent2ndaddr(int, char **, int *, struct nd_addrlist **);
148 static int ndaddr2hent(int, const char *, struct nd_addrlist *,
149     struct hostent *, char *, int);
150 static int hsents2ndhostservs(struct hostent *, struct servent *, ushort_t,
151     struct nd_hostservlist **);
152 static int ndaddr2srent(const char *, const char *, ushort_t, struct servent *,
153     char *, int);
154 static int ndhostserv2hent(struct netbuf *, struct nd_hostservlist *,
155     struct hostent *, char *, int);
156 static int ndhostserv2srent(int, const char *, struct nd_hostservlist *,
157     struct servent *, char *, int);
158 static int nd2herrno(int nerr);
159 static void order_haddrlist_inet(char **haddrlist, size_t addrcount);
160 static void order_haddrlist_inet6(char **haddrlist, size_t addrcount);
161 static int dstcmp(const void *, const void *);
162 static int nss_strioctl(int af, int cmd, void *ptr, int ilen);
163 static struct in_addr _inet_makeaddr(in_addr_t, in_addr_t);
164 static boolean_t _read_nsw_file(void);
165 
166 /*
167  * Begin: PART I
168  * Top Level Interfaces that gethost/serv/netdir funnel through.
169  */
170 
171 /*
172  * gethost/servbyname always call this function; if they call
173  * with nametoaddr libs in nconf, we call netdir_getbyname
174  * implementation: __classic_netdir_getbyname, otherwise nsswitch.
175  *
176  * netdir_getbyname calls this only if nametoaddr libs are NOT
177  * specified for inet transports; i.e. it's supposed to follow
178  * the name service switch.
179  */
180 int
181 _get_hostserv_inetnetdir_byname(struct netconfig *nconf,
182     struct nss_netdirbyname_in *args, union nss_netdirbyname_out *res)
183 {
184 	int	server_port;
185 	int *servp = &server_port;
186 	char	**haddrlist;
187 	uint32_t dotnameaddr;
188 	char	*dotnamelist[2];
189 	struct in_addr	*inaddrs = NULL;
190 	struct in6_addr	v6nameaddr;
191 	char	**baddrlist = NULL;
192 
193 
194 	if (nconf == NULL) {
195 		_nderror = ND_BADARG;
196 		return (ND_BADARG);
197 	}
198 
199 	/*
200 	 * 1. gethostbyname()/netdir_getbyname() special cases:
201 	 */
202 	switch (args->op_t) {
203 
204 		case NSS_HOST:
205 		/*
206 		 * Worth the performance gain -- assuming a lot of inet apps
207 		 * actively use "localhost".
208 		 */
209 		if (strcmp(args->arg.nss.host.name, LOCALHOST) == 0) {
210 
211 			(void) mutex_lock(&nd_addr_lock);
212 			IN_SET_LOOPBACK_ADDR(&sa_con);
213 			_nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
214 			    &nd_conaddrlist, res->nss.host.hent,
215 			    args->arg.nss.host.buf,
216 			    args->arg.nss.host.buflen);
217 			(void) mutex_unlock(&nd_addr_lock);
218 			if (_nderror != ND_OK)
219 				*(res->nss.host.herrno_p) =
220 				    nd2herrno(_nderror);
221 			return (_nderror);
222 		}
223 		/*
224 		 * If the caller passed in a dot separated IP notation to
225 		 * gethostbyname, return that back as the address.
226 		 * The nd_addr_lock mutex was added to be truely re-entrant.
227 		 */
228 		if (inet_aton(args->arg.nss.host.name,
229 		    (struct in_addr *)&dotnameaddr)) {
230 			(void) mutex_lock(&nd_addr_lock);
231 			(void) memset(&sa_con, 0, sizeof (sa_con));
232 			sa_con.sin_family = AF_INET;
233 			sa_con.sin_addr.s_addr = dotnameaddr;
234 			_nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
235 			    &nd_conaddrlist, res->nss.host.hent,
236 			    args->arg.nss.host.buf,
237 			    args->arg.nss.host.buflen);
238 			(void) mutex_unlock(&nd_addr_lock);
239 			if (_nderror != ND_OK)
240 				*(res->nss.host.herrno_p) =
241 				    nd2herrno(_nderror);
242 			return (_nderror);
243 		}
244 		break;
245 
246 		case NSS_HOST6:
247 		/*
248 		 * Handle case of literal address string.
249 		 */
250 		if (strchr(args->arg.nss.host6.name, ':') != NULL &&
251 		    (inet_pton(AF_INET6, args->arg.nss.host6.name,
252 		    &v6nameaddr) != 0)) {
253 			int	ret;
254 
255 			(void) mutex_lock(&nd6_addr_lock);
256 			(void) memset(&sa6_con, 0, sizeof (sa6_con));
257 			sa6_con.sin6_family = AF_INET6;
258 			(void) memcpy(&(sa6_con.sin6_addr.s6_addr),
259 			    &v6nameaddr, sizeof (struct in6_addr));
260 			ret = ndaddr2hent(AF_INET6,
261 			    args->arg.nss.host6.name,
262 			    &nd6_conaddrlist, res->nss.host.hent,
263 			    args->arg.nss.host6.buf,
264 			    args->arg.nss.host6.buflen);
265 			(void) mutex_unlock(&nd6_addr_lock);
266 			if (ret != ND_OK)
267 				*(res->nss.host.herrno_p) = nd2herrno(ret);
268 			else
269 				res->nss.host.hent->h_aliases = NULL;
270 			return (ret);
271 		}
272 		break;
273 
274 		case NETDIR_BY:
275 			if (args->arg.nd_hs == 0) {
276 				_nderror = ND_BADARG;
277 				return (ND_BADARG);
278 			}
279 			/*
280 			 * If servname is NULL, return 0 as the port number
281 			 * If servname is rpcbind, return 111 as the port number
282 			 * If servname is a number, return it back as the port
283 			 * number.
284 			 */
285 			if (args->arg.nd_hs->h_serv == 0) {
286 				*servp = htons(0);
287 			} else if (strcmp(args->arg.nd_hs->h_serv, "rpcbind")
288 									== 0) {
289 				*servp = htons(111);
290 			} else if (strspn(args->arg.nd_hs->h_serv, "0123456789")
291 				    == strlen(args->arg.nd_hs->h_serv)) {
292 				*servp = htons(atoi(args->arg.nd_hs->h_serv));
293 			} else {
294 				/* i.e. need to call a name service on this */
295 				servp = NULL;
296 			}
297 
298 			/*
299 			 * If the hostname is HOST_SELF_BIND, we return 0.0.0.0
300 			 * so the  binding can be contacted through all
301 			 * interfaces. If the hostname is HOST_SELF_CONNECT,
302 			 * we return 127.0.0.1 so the address can be connected
303 			 * to locally. If the hostname is HOST_ANY, we return
304 			 * no addresses because IP doesn't know how to specify
305 			 * a service without a host. And finally if we specify
306 			 * HOST_BROADCAST then we ask a tli fd to tell us what
307 			 * the broadcast addresses are for any udp
308 			 * interfaces on this machine.
309 			 */
310 			if (args->arg.nd_hs->h_host == 0) {
311 				_nderror = ND_NOHOST;
312 				return (ND_NOHOST);
313 			} else if ((strcmp(args->arg.nd_hs->h_host,
314 			    HOST_SELF_BIND) == 0)) {
315 				haddrlist = localaddr;
316 			} else if ((strcmp(args->arg.nd_hs->h_host,
317 					    HOST_SELF_CONNECT) == 0)) {
318 				haddrlist = connectaddr;
319 			} else if ((strcmp(args->arg.nd_hs->h_host,
320 					    LOCALHOST) == 0)) {
321 				haddrlist = connectaddr;
322 			} else if ((int)(dotnameaddr =
323 				    inet_addr(args->arg.nd_hs->h_host)) != -1) {
324 				/*
325 				 * If the caller passed in a dot separated IP
326 				 * notation to netdir_getbyname, convert that
327 				 * back into address.
328 				 */
329 
330 				dotnamelist[0] = (char *)&dotnameaddr;
331 				dotnamelist[1] = NULL;
332 				haddrlist = dotnamelist;
333 			} else if ((strcmp(args->arg.nd_hs->h_host,
334 					    HOST_BROADCAST) == 0)) {
335 				/*
336 				 * Now that inaddrs and baddrlist are
337 				 * dynamically allocated, care must be
338 				 * taken in freeing up the
339 				 * memory at each 'return()' point.
340 				 *
341 				 * Early return protection (using
342 				 * FREE_return()) is needed only in NETDIR_BY
343 				 * cases because dynamic allocation is used
344 				 * when args->op_t == NETDIR_BY.
345 				 *
346 				 * Early return protection is not needed in
347 				 * haddrlist==0 conditionals because dynamic
348 				 * allocation guarantees haddrlist!=0.
349 				 *
350 				 * Early return protection is not needed in most
351 				 * servp!=0 conditionals because this is handled
352 				 * (and returned) first.
353 				 */
354 #define	FREE_return(ret) \
355 				{ \
356 				    if (inaddrs) \
357 					    free(inaddrs); \
358 					    if (baddrlist) \
359 						    free(baddrlist); \
360 						    _nderror = ret; \
361 						    return (ret); \
362 				}
363 				int i, bnets;
364 
365 				bnets = getbroadcastnets(nconf, &inaddrs);
366 				if (bnets == 0) {
367 					_nderror = ND_NOHOST;
368 					return (ND_NOHOST);
369 				}
370 				baddrlist = malloc((bnets+1)*sizeof (char *));
371 				if (baddrlist == NULL)
372 					FREE_return(ND_NOMEM);
373 				for (i = 0; i < bnets; i++)
374 					baddrlist[i] = (char *)&inaddrs[i];
375 				baddrlist[i] = NULL;
376 				haddrlist = baddrlist;
377 			} else {
378 				/* i.e. need to call a name service on this */
379 				haddrlist = 0;
380 			}
381 
382 			if (haddrlist && servp) {
383 				int ret;
384 				/*
385 				 * Convert h_addr_list into nd_addrlist.
386 				 * malloc's will be done, freed using
387 				 * netdir_free.
388 				 */
389 				ret = hent2ndaddr(AF_INET, haddrlist, servp,
390 					    res->nd_alist);
391 				FREE_return(ret)
392 				}
393 			break;
394 
395 
396 		case NETDIR_BY6:
397 			if (args->arg.nd_hs == 0) {
398 				_nderror = ND_BADARG;
399 				return (ND_BADARG);
400 			}
401 			/*
402 			 * If servname is NULL, return 0 as the port number.
403 			 * If servname is rpcbind, return 111 as the port number
404 			 * If servname is a number, return it back as the port
405 			 * number.
406 			 */
407 			if (args->arg.nd_hs->h_serv == 0) {
408 				*servp = htons(0);
409 			} else if (strcmp(args->arg.nd_hs->h_serv,
410 				    "rpcbind") == 0) {
411 				*servp = htons(111);
412 			} else if (strspn(args->arg.nd_hs->h_serv, "0123456789")
413 				    == strlen(args->arg.nd_hs->h_serv)) {
414 				*servp = htons(atoi(args->arg.nd_hs->h_serv));
415 			} else {
416 				/* i.e. need to call a name service on this */
417 				servp = NULL;
418 			}
419 
420 			/*
421 			 * If the hostname is HOST_SELF_BIND, we return ipv6
422 			 * localaddress so the binding can be contacted through
423 			 * all interfaces.
424 			 * If the hostname is HOST_SELF_CONNECT, we return
425 			 * ipv6 loopback address so the address can be connected
426 			 * to locally.
427 			 * If the hostname is HOST_ANY, we return no addresses
428 			 * because IP doesn't know how to specify a service
429 			 * without a host.
430 			 * And finally if we specify HOST_BROADCAST then we
431 			 * disallow since IPV6 does not have any
432 			 * broadcast concept.
433 			 */
434 			if (args->arg.nd_hs->h_host == 0) {
435 				return (ND_NOHOST);
436 			} else if ((strcmp(args->arg.nd_hs->h_host,
437 					    HOST_SELF_BIND) == 0)) {
438 				haddrlist = localaddr6;
439 			} else if ((strcmp(args->arg.nd_hs->h_host,
440 					    HOST_SELF_CONNECT) == 0)) {
441 				haddrlist = connectaddr6;
442 			} else if ((strcmp(args->arg.nd_hs->h_host,
443 					    LOCALHOST) == 0)) {
444 				haddrlist = connectaddr6;
445 			} else if (strchr(args->arg.nd_hs->h_host, ':')
446 						    != NULL) {
447 
448 			/*
449 			 * If the caller passed in a dot separated IP notation
450 			 * to netdir_getbyname, convert that back into address.
451 			 */
452 
453 				if ((inet_pton(AF_INET6,
454 					    args->arg.nd_hs->h_host,
455 					    &v6nameaddr)) != 0) {
456 					dotnamelist[0] = (char *)&v6nameaddr;
457 					dotnamelist[1] = NULL;
458 					haddrlist = dotnamelist;
459 				}
460 				else
461 					/* not sure what to return */
462 					return (ND_NOHOST);
463 
464 			} else if ((strcmp(args->arg.nd_hs->h_host,
465 				    HOST_BROADCAST) == 0)) {
466 				/*
467 				 * Don't support broadcast in
468 				 * IPV6
469 				 */
470 				return (ND_NOHOST);
471 			} else {
472 				/* i.e. need to call a name service on this */
473 				haddrlist = 0;
474 			}
475 
476 			if (haddrlist && servp) {
477 				int ret;
478 				/*
479 				 * Convert h_addr_list into nd_addrlist.
480 				 * malloc's will be done, freed
481 				 * using netdir_free.
482 				 */
483 				ret = hent2ndaddr(AF_INET6, haddrlist,
484 				    servp, res->nd_alist);
485 				FREE_return(ret)
486 				}
487 			break;
488 
489 
490 	}
491 
492 	/*
493 	 * 2. Most common scenario. This is the way we ship /etc/netconfig.
494 	 *    Emphasis on improving performance in the "if" part.
495 	 */
496 	if (nconf->nc_nlookups == 0) {
497 		struct hostent	*he = NULL, *tmphe;
498 		struct servent	*se;
499 		int	ret;
500 		nss_XbyY_buf_t	*ndbuf4switch = 0;
501 
502 	switch (args->op_t) {
503 
504 		case NSS_HOST:
505 
506 		he = DOOR_GETHOSTBYNAME_R(args->arg.nss.host.name,
507 		    res->nss.host.hent, args->arg.nss.host.buf,
508 		    args->arg.nss.host.buflen,
509 		    res->nss.host.herrno_p);
510 		if (he == NULL)
511 			return (_nderror = ND_NOHOST);
512 		return (_nderror = ND_OK);
513 
514 		case NSS_HOST6:
515 
516 		he = DOOR_GETIPNODEBYNAME_R(args->arg.nss.host6.name,
517 		    res->nss.host.hent, args->arg.nss.host.buf,
518 		    args->arg.nss.host6.buflen,
519 		    args->arg.nss.host6.af_family,
520 		    args->arg.nss.host6.flags,
521 		    res->nss.host.herrno_p);
522 
523 		if (he == NULL)
524 			return (_nderror = ND_NOHOST);
525 		return (_nderror = ND_OK);
526 
527 		case NSS_SERV:
528 
529 		se = _switch_getservbyname_r(args->arg.nss.serv.name,
530 		    args->arg.nss.serv.proto,
531 		    res->nss.serv, args->arg.nss.serv.buf,
532 		    args->arg.nss.serv.buflen);
533 
534 		_nderror = ND_OK;
535 		if (se == 0)
536 			_nderror = ND_NOSERV;
537 		return (_nderror);
538 
539 		case NETDIR_BY:
540 
541 		if (servp == 0) {
542 			char	*proto =
543 	    (strcmp(nconf->nc_proto, NC_TCP) == 0) ? NC_TCP : NC_UDP;
544 
545 			/*
546 			 * We go through all this for just one port number,
547 			 * which is most often constant. How about linking in
548 			 * an indexed database of well-known ports in the name
549 			 * of performance ?
550 			 */
551 			ndbuf4switch = _nss_XbyY_buf_alloc(
552 				sizeof (struct servent), NSS_BUFLEN_SERVICES);
553 			if (ndbuf4switch == 0)
554 				FREE_return(ND_NOMEM);
555 			se = _switch_getservbyname_r(args->arg.nd_hs->h_serv,
556 				proto, ndbuf4switch->result,
557 				ndbuf4switch->buffer, ndbuf4switch->buflen);
558 			if (!se) {
559 				NSS_XbyY_FREE(&ndbuf4switch);
560 				FREE_return(ND_NOSERV)
561 			}
562 			server_port = se->s_port;
563 			NSS_XbyY_FREE(&ndbuf4switch);
564 		}
565 
566 		if (haddrlist == 0) {
567 			int	h_errnop = 0;
568 
569 			ndbuf4switch = _nss_XbyY_buf_alloc(
570 					sizeof (struct hostent),
571 					NSS_BUFLEN_HOSTS);
572 			if (ndbuf4switch == 0) {
573 				_nderror = ND_NOMEM;
574 				return (ND_NOMEM);
575 			}
576 			/*
577 			 * Search the ipnodes (v6) path first,
578 			 * search will return the v4 addresses
579 			 * as v4mapped addresses.
580 			 */
581 			if ((tmphe = DOOR_GETIPNODEBYNAME_R(
582 			    args->arg.nd_hs->h_host,
583 			    ndbuf4switch->result, ndbuf4switch->buffer,
584 			    ndbuf4switch->buflen, args->arg.nss.host6.af_family,
585 			    args->arg.nss.host6.flags, &h_errnop)) != NULL)
586 				he = __mappedtov4(tmphe, &h_errnop);
587 
588 			if (he == NULL) {
589 				/* Failover case, try hosts db for v4 address */
590 				he = DOOR_GETHOSTBYNAME_R(
591 				args->arg.nd_hs->h_host,
592 				ndbuf4switch->result, ndbuf4switch->buffer,
593 				ndbuf4switch->buflen, &h_errnop);
594 				if (he == NULL) {
595 					NSS_XbyY_FREE(&ndbuf4switch);
596 					_nderror = h_errnop ?
597 					    __herrno2netdir(h_errnop) :
598 					    ND_NOHOST;
599 					return (_nderror);
600 				}
601 				/*
602 				 * Convert h_addr_list into nd_addrlist.
603 				 * malloc's will be done, freed using
604 				 * netdir_free.
605 				 */
606 				ret = hent2ndaddr(AF_INET, he->h_addr_list,
607 					&server_port, res->nd_alist);
608 			} else {
609 				/*
610 				 * Convert h_addr_list into nd_addrlist.
611 				 * malloc's will be done, freed using
612 				 * netdir_free.
613 				 */
614 				ret = hent2ndaddr(AF_INET, he->h_addr_list,
615 					&server_port, res->nd_alist);
616 				freehostent(he);
617 			}
618 
619 			_nderror = ret;
620 			NSS_XbyY_FREE(&ndbuf4switch);
621 			return (ret);
622 		} else {
623 			int ret;
624 			/*
625 			 * Convert h_addr_list into nd_addrlist.
626 			 * malloc's will be done, freed using netdir_free.
627 			 */
628 			ret = hent2ndaddr(AF_INET, haddrlist,
629 				    &server_port, res->nd_alist);
630 			FREE_return(ret)
631 		}
632 
633 
634 		case NETDIR_BY6:
635 
636 			if (servp == 0) {
637 				char	*proto =
638 	(strcmp(nconf->nc_proto, NC_TCP) == 0) ? NC_TCP : NC_UDP;
639 
640 				/*
641 				 * We go through all this for just
642 				 * one port number,
643 				 * which is most often constant.
644 				 * How about linking in
645 				 * an indexed database of well-known
646 				 * ports in the name
647 				 * of performance ?
648 				 */
649 				ndbuf4switch = _nss_XbyY_buf_alloc(
650 					sizeof (struct servent),
651 					NSS_BUFLEN_SERVICES);
652 				if (ndbuf4switch == 0)
653 					FREE_return(ND_NOMEM);
654 				se = _switch_getservbyname_r(
655 					    args->arg.nd_hs->h_serv,
656 				    proto, ndbuf4switch->result,
657 				    ndbuf4switch->buffer, ndbuf4switch->buflen);
658 				if (!se) {
659 					NSS_XbyY_FREE(&ndbuf4switch);
660 					FREE_return(ND_NOSERV)
661 				}
662 				server_port = se->s_port;
663 				NSS_XbyY_FREE(&ndbuf4switch);
664 			}
665 
666 			if (haddrlist == 0) {
667 				int	h_errnop = 0;
668 
669 				ndbuf4switch = _nss_XbyY_buf_alloc(
670 					sizeof (struct hostent),
671 					NSS_BUFLEN_HOSTS);
672 				if (ndbuf4switch == 0) {
673 					_nderror = ND_NOMEM;
674 					return (ND_NOMEM);
675 				}
676 				he = DOOR_GETIPNODEBYNAME_R(
677 				    args->arg.nd_hs->h_host,
678 				    ndbuf4switch->result, ndbuf4switch->buffer,
679 				    ndbuf4switch->buflen,
680 				    args->arg.nss.host6.af_family,
681 				    args->arg.nss.host6.flags, &h_errnop);
682 				if (he == NULL) {
683 					NSS_XbyY_FREE(&ndbuf4switch);
684 					_nderror = h_errnop ?
685 					    __herrno2netdir(h_errnop) :
686 					    ND_NOHOST;
687 					return (_nderror);
688 				}
689 				/*
690 				 * Convert h_addr_list into nd_addrlist.
691 				 * malloc's will be done,
692 				 * freed using netdir_free.
693 				 */
694 				ret = hent2ndaddr(AF_INET6,
695 		    ((struct hostent *)(ndbuf4switch->result))->h_addr_list,
696 				    &server_port, res->nd_alist);
697 				_nderror = ret;
698 				NSS_XbyY_FREE(&ndbuf4switch);
699 				return (ret);
700 			} else {
701 				int ret;
702 				/*
703 				 * Convert h_addr_list into nd_addrlist.
704 				 * malloc's will be done,
705 				 * freed using netdir_free.
706 				 */
707 				ret = hent2ndaddr(AF_INET6, haddrlist,
708 					    &server_port, res->nd_alist);
709 				FREE_return(ret)
710 			}
711 
712 		default:
713 		_nderror = ND_BADARG;
714 		return (ND_BADARG); /* should never happen */
715 	}
716 
717 	} else {
718 		/* haddrlist is no longer used, so clean up */
719 		if (inaddrs)
720 			free(inaddrs);
721 		if (baddrlist)
722 			free(baddrlist);
723 	}
724 
725 	/*
726 	 * 3. We come this far only if nametoaddr libs are specified for
727 	 *    inet transports and we are called by gethost/servbyname only.
728 	 */
729 	switch (args->op_t) {
730 		struct	nd_hostserv service;
731 		struct	nd_addrlist *addrs;
732 		int ret;
733 
734 		case NSS_HOST:
735 
736 		service.h_host = (char *)args->arg.nss.host.name;
737 		service.h_serv = NULL;
738 		if ((_nderror = __classic_netdir_getbyname(nconf,
739 			    &service, &addrs)) != ND_OK) {
740 			*(res->nss.host.herrno_p) = nd2herrno(_nderror);
741 			return (_nderror);
742 		}
743 		/*
744 		 * convert addresses back into sockaddr for gethostbyname.
745 		 */
746 		ret = ndaddr2hent(AF_INET, service.h_host, addrs,
747 		    res->nss.host.hent, args->arg.nss.host.buf,
748 		    args->arg.nss.host.buflen);
749 		if (ret != ND_OK)
750 			*(res->nss.host.herrno_p) = nd2herrno(ret);
751 		netdir_free((char *)addrs, ND_ADDRLIST);
752 		_nderror = ret;
753 		return (ret);
754 
755 		case NSS_SERV:
756 
757 		if (args->arg.nss.serv.proto == NULL) {
758 			/*
759 			 * A similar HACK showed up in Solaris 2.3.
760 			 * The caller wild-carded proto -- i.e. will
761 			 * accept a match using tcp or udp for the port
762 			 * number. Since we have no hope of getting
763 			 * directly to a name service switch backend
764 			 * from here that understands this semantics,
765 			 * we try calling the netdir interfaces first
766 			 * with "tcp" and then "udp".
767 			 */
768 			args->arg.nss.serv.proto = "tcp";
769 			_nderror = _get_hostserv_inetnetdir_byname(nconf, args,
770 			    res);
771 			if (_nderror != ND_OK) {
772 				args->arg.nss.serv.proto = "udp";
773 				_nderror =
774 				    _get_hostserv_inetnetdir_byname(nconf,
775 				    args, res);
776 			}
777 			return (_nderror);
778 		}
779 
780 		/*
781 		 * Third-parties should optimize their nametoaddr
782 		 * libraries for the HOST_SELF case.
783 		 */
784 		service.h_host = HOST_SELF;
785 		service.h_serv = (char *)args->arg.nss.serv.name;
786 		if ((_nderror = __classic_netdir_getbyname(nconf,
787 			    &service, &addrs)) != ND_OK) {
788 			return (_nderror);
789 		}
790 		/*
791 		 * convert addresses back into servent for getservbyname.
792 		 */
793 		_nderror = ndaddr2srent(service.h_serv,
794 		    args->arg.nss.serv.proto,
795 		    /* LINTED pointer cast */
796 		    ((struct sockaddr_in *)addrs->n_addrs->buf)->sin_port,
797 		    res->nss.serv,
798 		    args->arg.nss.serv.buf, args->arg.nss.serv.buflen);
799 		netdir_free((char *)addrs, ND_ADDRLIST);
800 		return (_nderror);
801 
802 		default:
803 		_nderror = ND_BADARG;
804 		return (ND_BADARG); /* should never happen */
805 	}
806 }
807 
808 /*
809  * gethostbyaddr/servbyport always call this function; if they call
810  * with nametoaddr libs in nconf, we call netdir_getbyaddr
811  * implementation __classic_netdir_getbyaddr, otherwise nsswitch.
812  *
813  * netdir_getbyaddr calls this only if nametoaddr libs are NOT
814  * specified for inet transports; i.e. it's supposed to follow
815  * the name service switch.
816  */
817 int
818 _get_hostserv_inetnetdir_byaddr(struct netconfig *nconf,
819     struct nss_netdirbyaddr_in *args, union nss_netdirbyaddr_out *res)
820 {
821 	if (nconf == 0) {
822 		_nderror = ND_BADARG;
823 		return (_nderror);
824 	}
825 
826 	/*
827 	 * 1. gethostbyaddr()/netdir_getbyaddr() special cases:
828 	 */
829 	switch (args->op_t) {
830 
831 		case NSS_HOST:
832 		/*
833 		 * Worth the performance gain: assuming a lot of inet apps
834 		 * actively use "127.0.0.1".
835 		 */
836 		/* LINTED pointer cast */
837 		if (*(uint32_t *)(args->arg.nss.host.addr) ==
838 					htonl(INADDR_LOOPBACK)) {
839 			(void) mutex_lock(&nd_addr_lock);
840 			IN_SET_LOOPBACK_ADDR(&sa_con);
841 			_nderror = ndaddr2hent(AF_INET, LOCALHOST,
842 			    &nd_conaddrlist, res->nss.host.hent,
843 			    args->arg.nss.host.buf,
844 			    args->arg.nss.host.buflen);
845 			(void) mutex_unlock(&nd_addr_lock);
846 			if (_nderror != ND_OK)
847 				*(res->nss.host.herrno_p) =
848 				    nd2herrno(_nderror);
849 			return (_nderror);
850 		}
851 		break;
852 
853 		case NETDIR_BY:
854 		case NETDIR_BY_NOSRV:
855 		{
856 			struct sockaddr_in *sin;
857 
858 			if (args->arg.nd_nbuf == NULL) {
859 				_nderror = ND_BADARG;
860 				return (_nderror);
861 			}
862 
863 			/*
864 			 * Validate the address which was passed
865 			 * as the request.
866 			 */
867 			/* LINTED pointer cast */
868 			sin = (struct sockaddr_in *)args->arg.nd_nbuf->buf;
869 
870 			if ((args->arg.nd_nbuf->len !=
871 				sizeof (struct sockaddr_in)) ||
872 			    (sin->sin_family != AF_INET)) {
873 				_nderror = ND_BADARG;
874 				return (_nderror);
875 			}
876 		}
877 		break;
878 
879 		case NETDIR_BY6:
880 		case NETDIR_BY_NOSRV6:
881 		{
882 			struct sockaddr_in6 *sin6;
883 
884 			if (args->arg.nd_nbuf == NULL) {
885 				_nderror = ND_BADARG;
886 				return (_nderror);
887 			}
888 
889 			/*
890 			 * Validate the address which was passed
891 			 * as the request.
892 			 */
893 			/* LINTED pointer cast */
894 			sin6 = (struct sockaddr_in6 *)args->arg.nd_nbuf->buf;
895 
896 			if ((args->arg.nd_nbuf->len !=
897 				sizeof (struct sockaddr_in6)) ||
898 			    (sin6->sin6_family != AF_INET6)) {
899 				_nderror = ND_BADARG;
900 				return (_nderror);
901 			}
902 		}
903 		break;
904 
905 	}
906 
907 	/*
908 	 * 2. Most common scenario. This is the way we ship /etc/netconfig.
909 	 *    Emphasis on improving performance in the "if" part.
910 	 */
911 	if (nconf->nc_nlookups == 0) {
912 		struct hostent	*he = NULL, *tmphe;
913 		struct servent	*se = NULL;
914 		nss_XbyY_buf_t	*ndbuf4host = 0;
915 		nss_XbyY_buf_t	*ndbuf4serv = 0;
916 		char	*proto =
917 		    (strcmp(nconf->nc_proto, NC_TCP) == 0) ? NC_TCP : NC_UDP;
918 		struct	sockaddr_in *sa;
919 		struct sockaddr_in6 *sin6;
920 		struct in_addr *addr4 = 0;
921 		struct in6_addr v4mapbuf;
922 		int	h_errnop;
923 
924 	switch (args->op_t) {
925 
926 		case NSS_HOST:
927 
928 		he = DOOR_GETHOSTBYADDR_R(args->arg.nss.host.addr,
929 		    args->arg.nss.host.len, args->arg.nss.host.type,
930 		    res->nss.host.hent, args->arg.nss.host.buf,
931 		    args->arg.nss.host.buflen,
932 		    res->nss.host.herrno_p);
933 		if (he == 0)
934 			_nderror = ND_NOHOST;
935 		else
936 			_nderror = ND_OK;
937 		return (_nderror);
938 
939 
940 		case NSS_HOST6:
941 		he = DOOR_GETIPNODEBYADDR_R(args->arg.nss.host.addr,
942 		    args->arg.nss.host.len, args->arg.nss.host.type,
943 		    res->nss.host.hent, args->arg.nss.host.buf,
944 		    args->arg.nss.host.buflen,
945 		    res->nss.host.herrno_p);
946 
947 		if (he == 0)
948 			return (ND_NOHOST);
949 		return (ND_OK);
950 
951 
952 		case NSS_SERV:
953 
954 		se = _switch_getservbyport_r(args->arg.nss.serv.port,
955 		    args->arg.nss.serv.proto,
956 		    res->nss.serv, args->arg.nss.serv.buf,
957 		    args->arg.nss.serv.buflen);
958 
959 		if (se == 0)
960 			_nderror = ND_NOSERV;
961 		else
962 			_nderror = ND_OK;
963 		return (_nderror);
964 
965 		case NETDIR_BY:
966 		case NETDIR_BY_NOSRV:
967 
968 		ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
969 					NSS_BUFLEN_SERVICES);
970 		if (ndbuf4serv == 0) {
971 			_nderror = ND_NOMEM;
972 			return (_nderror);
973 		}
974 		/* LINTED pointer cast */
975 		sa = (struct sockaddr_in *)(args->arg.nd_nbuf->buf);
976 		addr4 = (struct in_addr *)&(sa->sin_addr);
977 
978 		/*
979 		 * if NETDIR_BY_NOSRV or port == 0 skip the service
980 		 * lookup.
981 		 */
982 		if (args->op_t != NETDIR_BY_NOSRV && sa->sin_port != 0) {
983 			se = _switch_getservbyport_r(sa->sin_port, proto,
984 			    ndbuf4serv->result, ndbuf4serv->buffer,
985 				    ndbuf4serv->buflen);
986 			if (!se) {
987 				NSS_XbyY_FREE(&ndbuf4serv);
988 				/*
989 				 * We can live with this - i.e. the address
990 				 * does not
991 				 * belong to a well known service. The caller
992 				 * traditionally accepts a stringified port
993 				 * number
994 				 * as the service name. The state of se is used
995 				 * ahead to indicate the same.
996 				 * However, we do not tolerate this nonsense
997 				 * when we cannot get a host name. See below.
998 				 */
999 			}
1000 		}
1001 
1002 		ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
1003 					NSS_BUFLEN_HOSTS);
1004 		if (ndbuf4host == 0) {
1005 			if (ndbuf4serv)
1006 				NSS_XbyY_FREE(&ndbuf4serv);
1007 			_nderror = ND_NOMEM;
1008 			return (_nderror);
1009 		}
1010 
1011 		/*
1012 		 * Since we're going to search the ipnodes (v6) path first,
1013 		 * we need to treat the address as a v4mapped address.
1014 		 */
1015 
1016 		IN6_INADDR_TO_V4MAPPED(addr4, &v4mapbuf);
1017 		if ((tmphe = DOOR_GETIPNODEBYADDR_R((char *)&v4mapbuf,
1018 		    16, AF_INET6, ndbuf4host->result,
1019 			    ndbuf4host->buffer,
1020 			    ndbuf4host->buflen, &h_errnop)) != NULL)
1021 			he = __mappedtov4(tmphe, &h_errnop);
1022 
1023 		if (!he) {
1024 			/* Failover case, try hosts db for v4 address */
1025 			he = DOOR_GETHOSTBYADDR_R((char *)
1026 					&(sa->sin_addr.s_addr), 4,
1027 					sa->sin_family, ndbuf4host->result,
1028 					ndbuf4host->buffer, ndbuf4host->buflen,
1029 					&h_errnop);
1030 			if (!he) {
1031 				NSS_XbyY_FREE(&ndbuf4host);
1032 				if (ndbuf4serv)
1033 					NSS_XbyY_FREE(&ndbuf4serv);
1034 				_nderror = __herrno2netdir(h_errnop);
1035 				return (_nderror);
1036 			}
1037 			/*
1038 			 * Convert host names and service names into hostserv
1039 			 * pairs. malloc's will be done, freed using
1040 			 * netdir_free.
1041 			 */
1042 			h_errnop = hsents2ndhostservs(he, se,
1043 			    sa->sin_port, res->nd_hslist);
1044 		} else {
1045 			/*
1046 			 * Convert host names and service names into hostserv
1047 			 * pairs. malloc's will be done, freed using
1048 			 * netdir_free.
1049 			 */
1050 			h_errnop = hsents2ndhostservs(he, se,
1051 			    sa->sin_port, res->nd_hslist);
1052 			freehostent(he);
1053 		}
1054 
1055 		NSS_XbyY_FREE(&ndbuf4host);
1056 		if (ndbuf4serv)
1057 		    NSS_XbyY_FREE(&ndbuf4serv);
1058 		_nderror = __herrno2netdir(h_errnop);
1059 		return (_nderror);
1060 
1061 		case NETDIR_BY6:
1062 		case NETDIR_BY_NOSRV6:
1063 
1064 		ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
1065 					NSS_BUFLEN_SERVICES);
1066 		if (ndbuf4serv == 0) {
1067 			_nderror = ND_NOMEM;
1068 			return (ND_NOMEM);
1069 		}
1070 		/* LINTED pointer cast */
1071 		sin6 = (struct sockaddr_in6 *)(args->arg.nd_nbuf->buf);
1072 
1073 		/*
1074 		 * if NETDIR_BY_NOSRV6 or port == 0 skip the service
1075 		 * lookup.
1076 		 */
1077 		if (args->op_t != NETDIR_BY_NOSRV6 && sin6->sin6_port == 0) {
1078 			se = _switch_getservbyport_r(sin6->sin6_port, proto,
1079 			    ndbuf4serv->result, ndbuf4serv->buffer,
1080 				    ndbuf4serv->buflen);
1081 			if (!se) {
1082 				NSS_XbyY_FREE(&ndbuf4serv);
1083 				/*
1084 				 * We can live with this - i.e. the address does
1085 				 * not * belong to a well known service. The
1086 				 * caller traditionally accepts a stringified
1087 				 * port number
1088 				 * as the service name. The state of se is used
1089 				 * ahead to indicate the same.
1090 				 * However, we do not tolerate this nonsense
1091 				 * when we cannot get a host name. See below.
1092 				 */
1093 			}
1094 		}
1095 
1096 		ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
1097 					NSS_BUFLEN_HOSTS);
1098 		if (ndbuf4host == 0) {
1099 			if (ndbuf4serv)
1100 				NSS_XbyY_FREE(&ndbuf4serv);
1101 			_nderror = ND_NOMEM;
1102 			return (_nderror);
1103 		}
1104 		he = DOOR_GETIPNODEBYADDR_R((char *)&(sin6->sin6_addr),
1105 		    16, sin6->sin6_family, ndbuf4host->result,
1106 			    ndbuf4host->buffer,
1107 			    ndbuf4host->buflen, &h_errnop);
1108 		if (!he) {
1109 			NSS_XbyY_FREE(&ndbuf4host);
1110 			if (ndbuf4serv)
1111 			    NSS_XbyY_FREE(&ndbuf4serv);
1112 			_nderror = __herrno2netdir(h_errnop);
1113 			return (_nderror);
1114 		}
1115 		/*
1116 		 * Convert host names and service names into hostserv
1117 		 * pairs. malloc's will be done, freed using netdir_free.
1118 		 */
1119 		h_errnop = hsents2ndhostservs(he, se,
1120 		    sin6->sin6_port, res->nd_hslist);
1121 
1122 		NSS_XbyY_FREE(&ndbuf4host);
1123 		if (ndbuf4serv)
1124 		    NSS_XbyY_FREE(&ndbuf4serv);
1125 		_nderror = __herrno2netdir(h_errnop);
1126 		return (_nderror);
1127 
1128 		default:
1129 		_nderror = ND_BADARG;
1130 		return (_nderror); /* should never happen */
1131 	}
1132 
1133 	}
1134 	/*
1135 	 * 3. We come this far only if nametoaddr libs are specified for
1136 	 *    inet transports and we are called by gethost/servbyname only.
1137 	 */
1138 	switch (args->op_t) {
1139 		struct	netbuf nbuf;
1140 		struct	nd_hostservlist *addrs;
1141 		struct	sockaddr_in sa;
1142 
1143 		case NSS_HOST:
1144 
1145 		/* LINTED pointer cast */
1146 		sa.sin_addr.s_addr = *(uint32_t *)args->arg.nss.host.addr;
1147 		sa.sin_family = AF_INET;
1148 		/* Hopefully, third-parties get this optimization */
1149 		sa.sin_port = 0;
1150 		nbuf.buf = (char *)&sa;
1151 		nbuf.len = nbuf.maxlen = sizeof (sa);
1152 		if ((_nderror = __classic_netdir_getbyaddr(nconf,
1153 			    &addrs, &nbuf)) != 0) {
1154 			*(res->nss.host.herrno_p) = nd2herrno(_nderror);
1155 			return (_nderror);
1156 		}
1157 		/*
1158 		 * convert the host-serv pairs into h_aliases and hent.
1159 		 */
1160 		_nderror = ndhostserv2hent(&nbuf, addrs, res->nss.host.hent,
1161 		    args->arg.nss.host.buf, args->arg.nss.host.buflen);
1162 		if (_nderror != ND_OK)
1163 			*(res->nss.host.herrno_p) = nd2herrno(_nderror);
1164 		netdir_free((char *)addrs, ND_HOSTSERVLIST);
1165 		return (_nderror);
1166 
1167 		case NSS_SERV:
1168 
1169 		if (args->arg.nss.serv.proto == NULL) {
1170 			/*
1171 			 * A similar HACK showed up in Solaris 2.3.
1172 			 * The caller wild-carded proto -- i.e. will
1173 			 * accept a match on tcp or udp for the port
1174 			 * number. Since we have no hope of getting
1175 			 * directly to a name service switch backend
1176 			 * from here that understands this semantics,
1177 			 * we try calling the netdir interfaces first
1178 			 * with "tcp" and then "udp".
1179 			 */
1180 			args->arg.nss.serv.proto = "tcp";
1181 			_nderror = _get_hostserv_inetnetdir_byaddr(nconf, args,
1182 			    res);
1183 			if (_nderror != ND_OK) {
1184 				args->arg.nss.serv.proto = "udp";
1185 				_nderror =
1186 				    _get_hostserv_inetnetdir_byaddr(nconf,
1187 					args, res);
1188 			}
1189 			return (_nderror);
1190 		}
1191 
1192 		/*
1193 		 * Third-party nametoaddr_libs should be optimized for
1194 		 * this case. It also gives a special semantics twist to
1195 		 * netdir_getbyaddr. Only for the INADDR_ANY case, it gives
1196 		 * higher priority to service lookups (over host lookups).
1197 		 * If service lookup fails, the backend returns ND_NOSERV to
1198 		 * facilitate lookup in the "next" naming service.
1199 		 * BugId: 1075403.
1200 		 */
1201 		sa.sin_addr.s_addr = INADDR_ANY;
1202 		sa.sin_family = AF_INET;
1203 		sa.sin_port = (ushort_t)args->arg.nss.serv.port;
1204 		sa.sin_zero[0] = '\0';
1205 		nbuf.buf = (char *)&sa;
1206 		nbuf.len = nbuf.maxlen = sizeof (sa);
1207 		if ((_nderror = __classic_netdir_getbyaddr(nconf,
1208 			    &addrs, &nbuf)) != ND_OK) {
1209 			return (_nderror);
1210 		}
1211 		/*
1212 		 * convert the host-serv pairs into s_aliases and servent.
1213 		 */
1214 		_nderror = ndhostserv2srent(args->arg.nss.serv.port,
1215 		    args->arg.nss.serv.proto, addrs, res->nss.serv,
1216 		    args->arg.nss.serv.buf, args->arg.nss.serv.buflen);
1217 		netdir_free((char *)addrs, ND_HOSTSERVLIST);
1218 		return (_nderror);
1219 
1220 		default:
1221 		_nderror = ND_BADARG;
1222 		return (_nderror); /* should never happen */
1223 	}
1224 }
1225 
1226 /*
1227  * Part II: Name Service Switch interfacing routines.
1228  */
1229 
1230 static DEFINE_NSS_DB_ROOT(db_root_hosts);
1231 static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
1232 static DEFINE_NSS_DB_ROOT(db_root_services);
1233 
1234 
1235 /*
1236  * There is a copy of __nss2herrno() in nsswitch/files/gethostent.c.
1237  * It is there because /etc/lib/nss_files.so.1 cannot call
1238  * routines in libnsl.  Care should be taken to keep the two copies
1239  * in sync.
1240  */
1241 int
1242 __nss2herrno(nss_status_t nsstat)
1243 {
1244 	switch (nsstat) {
1245 	case NSS_SUCCESS:
1246 		/* no macro-defined success code for h_errno */
1247 		return (0);
1248 	case NSS_NOTFOUND:
1249 		return (HOST_NOT_FOUND);
1250 	case NSS_TRYAGAIN:
1251 		return (TRY_AGAIN);
1252 	case NSS_UNAVAIL:
1253 		return (NO_RECOVERY);
1254 	}
1255 	/* NOTREACHED */
1256 	return (0);	/* keep gcc happy */
1257 }
1258 
1259 nss_status_t
1260 _herrno2nss(int h_errno)
1261 {
1262 	switch (h_errno) {
1263 	case 0:
1264 		return (NSS_SUCCESS);
1265 	case TRY_AGAIN:
1266 		return (NSS_TRYAGAIN);
1267 	case NO_RECOVERY:
1268 	case NETDB_INTERNAL:
1269 		return (NSS_UNAVAIL);
1270 	case HOST_NOT_FOUND:
1271 	case NO_DATA:
1272 	default:
1273 		return (NSS_NOTFOUND);
1274 	}
1275 }
1276 
1277 static int
1278 __herrno2netdir(int h_errnop)
1279 {
1280 	switch (h_errnop) {
1281 		case 0:
1282 			return (ND_OK);
1283 		case HOST_NOT_FOUND:
1284 			return (ND_NOHOST);
1285 		case TRY_AGAIN:
1286 			return (ND_TRY_AGAIN);
1287 		case NO_RECOVERY:
1288 		case NETDB_INTERNAL:
1289 			return (ND_NO_RECOVERY);
1290 		case NO_DATA:
1291 			return (ND_NO_DATA);
1292 		default:
1293 			return (ND_NOHOST);
1294 	}
1295 }
1296 
1297 /*
1298  * The _switch_getXXbyYY_r() routines should be static.  They used to
1299  * be exported in SunOS 5.3, and in fact publicised as work-around
1300  * interfaces for getting CNAME/aliases, and therefore, we preserve
1301  * their signatures here. Just in case.
1302  */
1303 
1304 struct hostent *
1305 _switch_gethostbyname_r(const char *name, struct hostent *result, char *buffer,
1306     int buflen, int *h_errnop)
1307 {
1308 	nss_XbyY_args_t arg;
1309 	nss_status_t	res;
1310 
1311 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
1312 	arg.key.name	= name;
1313 	arg.stayopen	= 0;
1314 	res = nss_search(&db_root_hosts, _nss_initf_hosts,
1315 	    NSS_DBOP_HOSTS_BYNAME, &arg);
1316 	arg.status = res;
1317 	*h_errnop = arg.h_errno;
1318 	if (arg.returnval != NULL)
1319 		order_haddrlist_af(result->h_addrtype, result->h_addr_list);
1320 	return ((struct hostent *)NSS_XbyY_FINI(&arg));
1321 }
1322 
1323 struct hostent *
1324 _switch_getipnodebyname_r(const char *name, struct hostent *result,
1325     char *buffer, int buflen, int af_family, int flags, int *h_errnop)
1326 {
1327 	nss_XbyY_args_t arg;
1328 	nss_status_t	res;
1329 
1330 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
1331 	arg.key.ipnode.name	= name;
1332 	arg.key.ipnode.af_family = af_family;
1333 	arg.key.ipnode.flags = flags;
1334 	arg.stayopen	= 0;
1335 	res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
1336 	    NSS_DBOP_IPNODES_BYNAME, &arg);
1337 	arg.status = res;
1338 	*h_errnop = arg.h_errno;
1339 	if (arg.returnval != NULL)
1340 		order_haddrlist_af(result->h_addrtype, result->h_addr_list);
1341 	return ((struct hostent *)NSS_XbyY_FINI(&arg));
1342 }
1343 
1344 struct hostent *
1345 _switch_gethostbyaddr_r(const char *addr, int len, int type,
1346     struct hostent *result, char *buffer, int buflen, int *h_errnop)
1347 {
1348 	nss_XbyY_args_t arg;
1349 	nss_status_t	res;
1350 
1351 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
1352 	arg.key.hostaddr.addr	= addr;
1353 	arg.key.hostaddr.len	= len;
1354 	arg.key.hostaddr.type	= type;
1355 	arg.stayopen		= 0;
1356 	res = nss_search(&db_root_hosts, _nss_initf_hosts,
1357 	    NSS_DBOP_HOSTS_BYADDR, &arg);
1358 	arg.status = res;
1359 	*h_errnop = arg.h_errno;
1360 	return (struct hostent *)NSS_XbyY_FINI(&arg);
1361 }
1362 
1363 struct hostent *
1364 _switch_getipnodebyaddr_r(const char *addr, int len, int type,
1365     struct hostent *result, char *buffer, int buflen, int *h_errnop)
1366 {
1367 	nss_XbyY_args_t arg;
1368 	nss_status_t	res;
1369 
1370 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
1371 	arg.key.hostaddr.addr	= addr;
1372 	arg.key.hostaddr.len	= len;
1373 	arg.key.hostaddr.type	= type;
1374 	arg.stayopen		= 0;
1375 	res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
1376 	    NSS_DBOP_IPNODES_BYADDR, &arg);
1377 	arg.status = res;
1378 	*h_errnop = arg.h_errno;
1379 	return (struct hostent *)NSS_XbyY_FINI(&arg);
1380 }
1381 
1382 static void
1383 _nss_initf_services(nss_db_params_t *p)
1384 {
1385 	p->name	= NSS_DBNAM_SERVICES;
1386 	p->default_config = NSS_DEFCONF_SERVICES;
1387 }
1388 
1389 struct servent *
1390 _switch_getservbyname_r(const char *name, const char *proto,
1391     struct servent *result, char *buffer, int buflen)
1392 {
1393 	nss_XbyY_args_t arg;
1394 	nss_status_t	res;
1395 
1396 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
1397 	arg.key.serv.serv.name	= name;
1398 	arg.key.serv.proto	= proto;
1399 	arg.stayopen		= 0;
1400 	res = nss_search(&db_root_services, _nss_initf_services,
1401 	    NSS_DBOP_SERVICES_BYNAME, &arg);
1402 	arg.status = res;
1403 	return ((struct servent *)NSS_XbyY_FINI(&arg));
1404 }
1405 
1406 struct servent *
1407 _switch_getservbyport_r(int port, const char *proto, struct servent *result,
1408     char *buffer, int buflen)
1409 {
1410 	nss_XbyY_args_t arg;
1411 	nss_status_t	res;
1412 
1413 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
1414 	arg.key.serv.serv.port	= port;
1415 	arg.key.serv.proto	= proto;
1416 	arg.stayopen		= 0;
1417 	res = nss_search(&db_root_services, _nss_initf_services,
1418 	    NSS_DBOP_SERVICES_BYPORT, &arg);
1419 	arg.status = res;
1420 	return ((struct servent *)NSS_XbyY_FINI(&arg));
1421 }
1422 
1423 
1424 /*
1425  * Return values: 0 = success, 1 = parse error, 2 = erange ...
1426  * The structure pointer passed in is a structure in the caller's space
1427  * wherein the field pointers would be set to areas in the buffer if
1428  * need be. instring and buffer should be separate areas.
1429  *
1430  * Defined here because we need it and we (libnsl) cannot have a dependency
1431  * on libsocket (however, libsocket always depends on libnsl).
1432  */
1433 int
1434 str2servent(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
1435 {
1436 	struct servent	*serv	= (struct servent *)ent;
1437 	const char	*p, *fieldstart, *limit, *namestart;
1438 	ssize_t		fieldlen, namelen = 0;
1439 	char		numbuf[12];
1440 	char		*numend;
1441 
1442 	if ((instr >= buffer && (buffer + buflen) > instr) ||
1443 	    (buffer >= instr && (instr + lenstr) > buffer)) {
1444 		return (NSS_STR_PARSE_PARSE);
1445 	}
1446 
1447 	p = instr;
1448 	limit = p + lenstr;
1449 
1450 	while (p < limit && isspace(*p)) {
1451 		p++;
1452 	}
1453 	namestart = p;
1454 	while (p < limit && !isspace(*p)) {
1455 		p++;		/* Skip over the canonical name */
1456 	}
1457 	namelen = p - namestart;
1458 
1459 	if (buflen <= namelen) { /* not enough buffer */
1460 		return (NSS_STR_PARSE_ERANGE);
1461 	}
1462 	(void) memcpy(buffer, namestart, namelen);
1463 	buffer[namelen] = '\0';
1464 	serv->s_name = buffer;
1465 
1466 	while (p < limit && isspace(*p)) {
1467 		p++;
1468 	}
1469 
1470 	fieldstart = p;
1471 	do {
1472 		if (p > limit || isspace(*p)) {
1473 			/* Syntax error -- no port/proto */
1474 			return (NSS_STR_PARSE_PARSE);
1475 		}
1476 	}
1477 	while (*p++ != '/');
1478 	fieldlen = p - fieldstart - 1;
1479 	if (fieldlen == 0 || fieldlen >= sizeof (numbuf)) {
1480 		/* Syntax error -- supposed number is empty or too long */
1481 		return (NSS_STR_PARSE_PARSE);
1482 	}
1483 	(void) memcpy(numbuf, fieldstart, fieldlen);
1484 	numbuf[fieldlen] = '\0';
1485 	serv->s_port = htons((int)strtol(numbuf, &numend, 10));
1486 	if (*numend != '\0') {
1487 		/* Syntax error -- port number isn't a number */
1488 		return (NSS_STR_PARSE_PARSE);
1489 	}
1490 
1491 	fieldstart = p;
1492 	while (p < limit && !isspace(*p)) {
1493 		p++;		/* Scan the protocol name */
1494 	}
1495 	fieldlen = p - fieldstart + 1;		/* Include '\0' this time */
1496 	if (fieldlen > buflen - namelen - 1) {
1497 		return (NSS_STR_PARSE_ERANGE);
1498 	}
1499 	serv->s_proto = buffer + namelen + 1;
1500 	(void) memcpy(serv->s_proto, fieldstart, fieldlen - 1);
1501 	serv->s_proto[fieldlen - 1] = '\0';
1502 
1503 	while (p < limit && isspace(*p)) {
1504 		p++;
1505 	}
1506 	/*
1507 	 * Although nss_files_XY_all calls us with # stripped,
1508 	 * we should be able to deal with it here in order to
1509 	 * be more useful.
1510 	 */
1511 	if (p >= limit || *p == '#') { /* no aliases, no problem */
1512 		char **ptr;
1513 
1514 		ptr = (char **)ROUND_UP(buffer + namelen + 1 + fieldlen,
1515 		    sizeof (char *));
1516 		if ((char *)ptr >= buffer + buflen) {
1517 			/* hope they don't try to peek in */
1518 			serv->s_aliases = 0;
1519 			return (NSS_STR_PARSE_ERANGE);
1520 		} else {
1521 			*ptr = 0;
1522 			serv->s_aliases = ptr;
1523 			return (NSS_STR_PARSE_SUCCESS);
1524 		}
1525 	}
1526 	serv->s_aliases = _nss_netdb_aliases(p, (int)(lenstr - (p - instr)),
1527 	    buffer + namelen + 1 + fieldlen,
1528 	    (int)(buflen - namelen - 1 - fieldlen));
1529 	return (NSS_STR_PARSE_SUCCESS);
1530 }
1531 
1532 /*
1533  * Part III: All `n sundry routines that are useful only in this
1534  * module. In the interest of keeping this source file shorter,
1535  * we would create them a new module only if the linker allowed
1536  * "library-static" functions.
1537  *
1538  * Routines to order addresses based on local interfaces and netmasks,
1539  * to get and check reserved ports, and to get broadcast nets.
1540  */
1541 
1542 union __v4v6addr {
1543 	struct in6_addr	in6;
1544 	struct in_addr	in4;
1545 };
1546 
1547 struct __ifaddr {
1548 	sa_family_t		af;
1549 	union __v4v6addr	addr;
1550 	union __v4v6addr	mask;
1551 };
1552 
1553 struct ifinfo {
1554 	int		count;
1555 	struct __ifaddr	*addresses;
1556 };
1557 
1558 typedef enum {ADDR_ONLINK = 0, ADDR_OFFLINK} addr_class_t;
1559 #define	ADDR_NUMCLASSES	2
1560 
1561 typedef enum {IF_ADDR, IF_MASK}	__ifaddr_type;
1562 static int	__inet_ifassign(sa_family_t, struct __ifaddr *, __ifaddr_type,
1563 				void *);
1564 int		__inet_address_is_local_af(void *, sa_family_t, void *);
1565 
1566 #define	ifaf(index)	(localinfo->addresses[index].af)
1567 #define	ifaddr4(index)	(localinfo->addresses[index].addr.in4)
1568 #define	ifaddr6(index)	(localinfo->addresses[index].addr.in6)
1569 #define	ifmask4(index)	(localinfo->addresses[index].mask.in4)
1570 #define	ifmask6(index)	(localinfo->addresses[index].mask.in6)
1571 #define	ifinfosize(n)	(sizeof (struct ifinfo) + (n)*sizeof (struct __ifaddr))
1572 
1573 #define	lifraddrp(lifr)	((lifr.lifr_addr.ss_family == AF_INET6) ? \
1574 	(void *)&((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr : \
1575 	(void *)&((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr)
1576 
1577 #define	ifassign(lifr, index, type) \
1578 			__inet_ifassign(lifr.lifr_addr.ss_family, \
1579 				&localinfo->addresses[index], type, \
1580 				lifraddrp(lifr))
1581 
1582 /*
1583  * The number of nanoseconds the order_haddrlist_inet() function waits
1584  * to retreive IP interface information.  The default is five minutes.
1585  */
1586 #define	IFINFOTIMEOUT	((hrtime_t)300 * NANOSEC)
1587 
1588 /*
1589  * Sort the addresses in haddrlist.  Since the sorting algorithms are
1590  * address-family specific, the work is done in the address-family
1591  * specific order_haddrlist_<family> functions.
1592  *
1593  * Do not sort addresses if SORT_ADDRS variable is set to NO or FALSE
1594  * in the configuration file /etc/default/nss. This is useful in case
1595  * the order of addresses returned by the nameserver needs to be
1596  * maintained. (DNS round robin feature is one example)
1597  */
1598 void
1599 order_haddrlist_af(sa_family_t af, char **haddrlist)
1600 {
1601 	size_t			addrcount;
1602 	char			**addrptr;
1603 	static boolean_t	checksortcfg = B_TRUE;
1604 	static boolean_t	nosort = B_FALSE;
1605 	static mutex_t		checksortcfg_lock = DEFAULTMUTEX;
1606 
1607 	if (haddrlist == NULL)
1608 		return;
1609 
1610 	/*
1611 	 * Check if SORT_ADDRS is set to NO or FALSE in the configuration
1612 	 * file.  We do not have to sort addresses in that case.
1613 	 */
1614 	(void) mutex_lock(&checksortcfg_lock);
1615 	if (checksortcfg == B_TRUE) {
1616 		checksortcfg = B_FALSE;
1617 		nosort = _read_nsw_file();
1618 	}
1619 	(void) mutex_unlock(&checksortcfg_lock);
1620 
1621 	if (nosort)
1622 		return;
1623 
1624 	/* Count the addresses to sort */
1625 	addrcount = 0;
1626 	for (addrptr = haddrlist; *addrptr != NULL; addrptr++)
1627 		addrcount++;
1628 
1629 	/*
1630 	 * If there's only one address or no addresses to sort, then
1631 	 * there's nothing for us to do.
1632 	 */
1633 	if (addrcount <= 1)
1634 		return;
1635 
1636 	/* Call the address-family specific sorting functions. */
1637 	switch (af) {
1638 	case AF_INET:
1639 		order_haddrlist_inet(haddrlist, addrcount);
1640 		break;
1641 	case AF_INET6:
1642 		order_haddrlist_inet6(haddrlist, addrcount);
1643 		break;
1644 	default:
1645 		break;
1646 	}
1647 }
1648 
1649 /*
1650  * Move any local (on-link) addresses toward the beginning of haddrlist.
1651  * The order within these two classes is preserved.
1652  *
1653  * The interface list is retrieved no more often than every
1654  * IFINFOTIMEOUT nanoseconds. Access to the interface list is
1655  * protected by an RW lock.
1656  *
1657  * If this function encounters an error, haddrlist is unaltered.
1658  */
1659 static void
1660 order_haddrlist_inet(char **haddrlist, size_t addrcount)
1661 {
1662 	static struct	ifinfo *localinfo = NULL;
1663 	static hrtime_t	then = 0; /* the last time localinfo was updated */
1664 	hrtime_t	now;
1665 	static rwlock_t	localinfo_lock = DEFAULTRWLOCK;
1666 	uint8_t		*sortbuf;
1667 	size_t		sortbuf_size;
1668 	struct in_addr	**inaddrlist = (struct in_addr **)haddrlist;
1669 	struct in_addr	**sorted;
1670 	struct in_addr	**classnext[ADDR_NUMCLASSES];
1671 	uint_t		classcount[ADDR_NUMCLASSES];
1672 	addr_class_t	*sortclass;
1673 	int		i;
1674 	int		rc;
1675 
1676 
1677 	/*
1678 	 * The classes in the sortclass array correspond to the class
1679 	 * of the address in the haddrlist list of the same index.
1680 	 * The classes are:
1681 	 *
1682 	 * ADDR_ONLINK	on-link address
1683 	 * ADDR_OFFLINK	off-link address
1684 	 */
1685 	sortbuf_size = addrcount *
1686 	    (sizeof (struct in_addr *) + sizeof (addr_class_t));
1687 	if ((sortbuf = malloc(sortbuf_size)) == NULL)
1688 		return;
1689 	/* LINTED pointer cast */
1690 	sorted = (struct in_addr **)sortbuf;
1691 	/* LINTED pointer cast */
1692 	sortclass = (addr_class_t *)(sortbuf +
1693 	    (addrcount * sizeof (struct in_addr *)));
1694 
1695 	/*
1696 	 * Get a read lock, and check if the interface information
1697 	 * is too old.
1698 	 */
1699 	(void) rw_rdlock(&localinfo_lock);
1700 	now = gethrtime();
1701 	if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
1702 		/* Need to update I/F info. Upgrade to write lock. */
1703 		(void) rw_unlock(&localinfo_lock);
1704 		(void) rw_wrlock(&localinfo_lock);
1705 		/*
1706 		 * Another thread might have updated "then" between
1707 		 * the rw_unlock() and rw_wrlock() calls above, so
1708 		 * re-check the timeout.
1709 		 */
1710 		if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
1711 			if (localinfo != NULL)
1712 				free(localinfo);
1713 			if ((localinfo = get_local_info()) == NULL) {
1714 				(void) rw_unlock(&localinfo_lock);
1715 				free(sortbuf);
1716 				return;
1717 			}
1718 			then = now;
1719 		}
1720 		/* Downgrade to read lock */
1721 		(void) rw_unlock(&localinfo_lock);
1722 		(void) rw_rdlock(&localinfo_lock);
1723 		/*
1724 		 * Another thread may have updated the I/F info,
1725 		 * so verify that the 'localinfo' pointer still
1726 		 * is non-NULL.
1727 		 */
1728 		if (localinfo == NULL) {
1729 			(void) rw_unlock(&localinfo_lock);
1730 			free(sortbuf);
1731 			return;
1732 		}
1733 	}
1734 
1735 	/*
1736 	 * Classify the addresses.  We also maintain the classcount
1737 	 * array to keep track of the number of addresses in each
1738 	 * class.
1739 	 */
1740 	(void) memset(classcount, 0, sizeof (classcount));
1741 	for (i = 0; i < addrcount; i++) {
1742 		if (__inet_address_is_local_af(localinfo, AF_INET,
1743 		    inaddrlist[i]))
1744 			sortclass[i] = ADDR_ONLINK;
1745 		else
1746 			sortclass[i] = ADDR_OFFLINK;
1747 		classcount[sortclass[i]]++;
1748 	}
1749 
1750 	/* Don't need the interface list anymore in this call */
1751 	(void) rw_unlock(&localinfo_lock);
1752 
1753 	/*
1754 	 * Each element in the classnext array points to the next
1755 	 * element for that class in the sorted address list. 'rc' is
1756 	 * the running count of elements as we sum the class
1757 	 * sub-totals.
1758 	 */
1759 	for (rc = 0, i = 0; i < ADDR_NUMCLASSES; i++) {
1760 		classnext[i] = &sorted[rc];
1761 		rc += classcount[i];
1762 	}
1763 
1764 	/* Now for the actual rearrangement of the addresses */
1765 	for (i = 0; i < addrcount; i++) {
1766 		*(classnext[sortclass[i]]) = inaddrlist[i];
1767 		classnext[sortclass[i]]++;
1768 	}
1769 
1770 	/* Copy the sorted list to inaddrlist */
1771 	(void) memcpy(inaddrlist, sorted,
1772 	    addrcount * sizeof (struct in_addr *));
1773 	free(sortbuf);
1774 }
1775 
1776 /*
1777  * This function implements the IPv6 Default Address Selection's
1778  * destination address ordering mechanism.  The algorithm is described
1779  * in getaddrinfo(3SOCKET).
1780  */
1781 static void
1782 order_haddrlist_inet6(char **haddrlist, size_t addrcount)
1783 {
1784 	struct dstinforeq *dinfo, *dinfoptr;
1785 	struct in6_addr **in6addrlist = (struct in6_addr **)haddrlist;
1786 	struct in6_addr	**in6addr;
1787 
1788 	if ((dinfo = calloc(addrcount, sizeof (struct dstinforeq))) == NULL)
1789 		return;
1790 
1791 	/* Initialize the dstinfo array we'll use for SIOCGDSTINFO */
1792 	dinfoptr = dinfo;
1793 	for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1794 		dinfoptr->dir_daddr = **in6addr;
1795 		dinfoptr++;
1796 	}
1797 
1798 	if (nss_strioctl(AF_INET6, SIOCGDSTINFO, dinfo,
1799 	    addrcount * sizeof (struct dstinforeq)) < 0) {
1800 		free(dinfo);
1801 		return;
1802 	}
1803 
1804 	/* Sort the dinfo array */
1805 	qsort(dinfo, addrcount, sizeof (struct dstinforeq), dstcmp);
1806 
1807 	/* Copy the addresses back into in6addrlist */
1808 	dinfoptr = dinfo;
1809 	for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1810 		**in6addr = dinfoptr->dir_daddr;
1811 		dinfoptr++;
1812 	}
1813 
1814 	free(dinfo);
1815 }
1816 
1817 /*
1818  * Determine number of leading bits that are common between two addresses.
1819  * Only consider bits which fall within the prefix length plen.
1820  */
1821 static uint_t
1822 ip_addr_commonbits_v6(const in6_addr_t *a1, const in6_addr_t *a2)
1823 {
1824 	uint_t		bits;
1825 	uint_t		i;
1826 	uint32_t	diff;	/* Bits that differ */
1827 
1828 	for (i = 0; i < 4; i++) {
1829 		if (a1->_S6_un._S6_u32[i] != a2->_S6_un._S6_u32[i])
1830 			break;
1831 	}
1832 	bits = i * 32;
1833 
1834 	if (bits == IPV6_ABITS)
1835 		return (IPV6_ABITS);
1836 
1837 	/*
1838 	 * Find number of leading common bits in the word which might
1839 	 * have some common bits by searching for the first one from the left
1840 	 * in the xor of the two addresses.
1841 	 */
1842 	diff = ntohl(a1->_S6_un._S6_u32[i] ^ a2->_S6_un._S6_u32[i]);
1843 	if (diff & 0xffff0000ul)
1844 		diff >>= 16;
1845 	else
1846 		bits += 16;
1847 	if (diff & 0xff00)
1848 		diff >>= 8;
1849 	else
1850 		bits += 8;
1851 	if (diff & 0xf0)
1852 		diff >>= 4;
1853 	else
1854 		bits += 4;
1855 	if (diff & 0xc)
1856 		diff >>= 2;
1857 	else
1858 		bits += 2;
1859 	if (!(diff & 2))
1860 		bits++;
1861 
1862 	/*
1863 	 * We don't need to shift and check for the last bit.  The
1864 	 * check for IPV6_ABITS above would have caught that.
1865 	 */
1866 
1867 	return (bits);
1868 }
1869 
1870 
1871 /*
1872  * The following group of functions named rule_*() are individual
1873  * sorting rules for the AF_INET6 address sorting algorithm.  The
1874  * functions compare two addresses (described by two dstinforeq
1875  * structures), and determines if one is "greater" than the other, or
1876  * if the two are equal according to that rule.
1877  */
1878 typedef	int (*rulef_t)(const struct dstinforeq *, const struct dstinforeq *);
1879 
1880 /*
1881  * These values of these constants are no accident.  Since qsort()
1882  * implements the AF_INET6 address sorting, the comparison function
1883  * must return an integer less than, equal to, or greater than zero to
1884  * indicate if the first address is considered "less than", "equal
1885  * to", or "greater than" the second one.  Since we want the best
1886  * addresses first on the list, "less than" is considered preferrable.
1887  */
1888 #define	RULE_PREFER_DA	-1
1889 #define	RULE_PREFER_DB	1
1890 #define	RULE_EQUAL	0
1891 
1892 /* Prefer the addresses that is reachable. */
1893 static int
1894 rule_reachable(const struct dstinforeq *da, const struct dstinforeq *db)
1895 {
1896 	if (da->dir_dreachable == db->dir_dreachable)
1897 		return (RULE_EQUAL);
1898 	if (da->dir_dreachable)
1899 		return (RULE_PREFER_DA);
1900 	return (RULE_PREFER_DB);
1901 }
1902 
1903 /* Prefer the address whose scope matches that of its source address. */
1904 static int
1905 rule_matchscope(const struct dstinforeq *da, const struct dstinforeq *db)
1906 {
1907 	boolean_t da_scope_match, db_scope_match;
1908 
1909 	da_scope_match = da->dir_dscope == da->dir_sscope;
1910 	db_scope_match = db->dir_dscope == db->dir_sscope;
1911 
1912 	if (da_scope_match == db_scope_match)
1913 		return (RULE_EQUAL);
1914 	if (da_scope_match)
1915 		return (RULE_PREFER_DA);
1916 	return (RULE_PREFER_DB);
1917 }
1918 
1919 /* Avoid the address with the link local source address. */
1920 static int
1921 rule_avoidlinklocal(const struct dstinforeq *da, const struct dstinforeq *db)
1922 {
1923 	if (da->dir_sscope == IP6_SCOPE_LINKLOCAL &&
1924 	    da->dir_dscope != IP6_SCOPE_LINKLOCAL &&
1925 	    db->dir_sscope != IP6_SCOPE_LINKLOCAL)
1926 		return (RULE_PREFER_DB);
1927 	if (db->dir_sscope == IP6_SCOPE_LINKLOCAL &&
1928 	    db->dir_dscope != IP6_SCOPE_LINKLOCAL &&
1929 	    da->dir_sscope != IP6_SCOPE_LINKLOCAL)
1930 		return (RULE_PREFER_DA);
1931 	return (RULE_EQUAL);
1932 }
1933 
1934 /* Prefer the address whose source address isn't deprecated. */
1935 static int
1936 rule_deprecated(const struct dstinforeq *da, const struct dstinforeq *db)
1937 {
1938 	if (da->dir_sdeprecated == db->dir_sdeprecated)
1939 		return (RULE_EQUAL);
1940 	if (db->dir_sdeprecated)
1941 		return (RULE_PREFER_DA);
1942 	return (RULE_PREFER_DB);
1943 }
1944 
1945 /* Prefer the address whose label matches that of its source address. */
1946 static int
1947 rule_label(const struct dstinforeq *da, const struct dstinforeq *db)
1948 {
1949 	if (da->dir_labelmatch == db->dir_labelmatch)
1950 		return (RULE_EQUAL);
1951 	if (da->dir_labelmatch)
1952 		return (RULE_PREFER_DA);
1953 	return (RULE_PREFER_DB);
1954 }
1955 
1956 /* Prefer the address with the higher precedence. */
1957 static int
1958 rule_precedence(const struct dstinforeq *da, const struct dstinforeq *db)
1959 {
1960 	if (da->dir_precedence == db->dir_precedence)
1961 		return (RULE_EQUAL);
1962 	if (da->dir_precedence > db->dir_precedence)
1963 		return (RULE_PREFER_DA);
1964 	return (RULE_PREFER_DB);
1965 }
1966 
1967 /* Prefer the address whose output interface isn't an IP tunnel */
1968 static int
1969 rule_native(const struct dstinforeq *da, const struct dstinforeq *db)
1970 {
1971 	boolean_t isatun, isbtun;
1972 
1973 	/* Get the common case out of the way early */
1974 	if (da->dir_dmactype == db->dir_dmactype)
1975 		return (RULE_EQUAL);
1976 
1977 	isatun = da->dir_dmactype == DL_IPV4 || da->dir_dmactype == DL_IPV6;
1978 	isbtun = db->dir_dmactype == DL_IPV4 || db->dir_dmactype == DL_IPV6;
1979 
1980 	if (isatun == isbtun)
1981 		return (RULE_EQUAL);
1982 	if (isbtun)
1983 		return (RULE_PREFER_DA);
1984 	return (RULE_PREFER_DB);
1985 }
1986 
1987 /* Prefer the address with the smaller scope. */
1988 static int
1989 rule_scope(const struct dstinforeq *da, const struct dstinforeq *db)
1990 {
1991 	if (da->dir_dscope == db->dir_dscope)
1992 		return (RULE_EQUAL);
1993 	if (da->dir_dscope < db->dir_dscope)
1994 		return (RULE_PREFER_DA);
1995 	return (RULE_PREFER_DB);
1996 }
1997 
1998 /*
1999  * Prefer the address that has the most leading bits in common with its
2000  * source address.
2001  */
2002 static int
2003 rule_prefix(const struct dstinforeq *da, const struct dstinforeq *db)
2004 {
2005 	uint_t da_commonbits, db_commonbits;
2006 	boolean_t da_isipv4, db_isipv4;
2007 
2008 	da_isipv4 = IN6_IS_ADDR_V4MAPPED(&da->dir_daddr);
2009 	db_isipv4 = IN6_IS_ADDR_V4MAPPED(&db->dir_daddr);
2010 
2011 	/*
2012 	 * At this point, the order doesn't matter if the two addresses
2013 	 * aren't of the same address family.
2014 	 */
2015 	if (da_isipv4 != db_isipv4)
2016 		return (RULE_EQUAL);
2017 
2018 	da_commonbits = ip_addr_commonbits_v6(&da->dir_daddr, &da->dir_saddr);
2019 	db_commonbits = ip_addr_commonbits_v6(&db->dir_daddr, &db->dir_saddr);
2020 
2021 	if (da_commonbits > db_commonbits)
2022 		return (RULE_PREFER_DA);
2023 	if (da_commonbits < db_commonbits)
2024 		return (RULE_PREFER_DB);
2025 	return (RULE_EQUAL);
2026 }
2027 
2028 /*
2029  * This is the function passed to qsort() that does the AF_INET6
2030  * address comparisons.  It compares two addresses using a list of
2031  * rules.  The rules are applied in order until one prefers one
2032  * address over the other.
2033  */
2034 static int
2035 dstcmp(const void *da, const void *db)
2036 {
2037 	int index, result;
2038 	rulef_t rules[] = {
2039 		rule_reachable,
2040 		rule_matchscope,
2041 		rule_avoidlinklocal,
2042 		rule_deprecated,
2043 		rule_label,
2044 		rule_precedence,
2045 		rule_native,
2046 		rule_scope,
2047 		rule_prefix,
2048 		NULL
2049 	};
2050 
2051 	result = 0;
2052 	for (index = 0; rules[index] != NULL; index++) {
2053 		result = (rules[index])(da, db);
2054 		if (result != RULE_EQUAL)
2055 			break;
2056 	}
2057 
2058 	return (result);
2059 }
2060 
2061 /*
2062  * Given haddrlist and a port number, mallocs and populates a new
2063  * nd_addrlist.  The new nd_addrlist maintains the order of the addresses
2064  * in haddrlist, which have already been sorted by order_haddrlist_inet()
2065  * or order_haddrlist_inet6().  For IPv6 this function filters out
2066  * IPv4-mapped IPv6 addresses.
2067  */
2068 int
2069 hent2ndaddr(int af, char **haddrlist, int *servp, struct nd_addrlist **nd_alist)
2070 {
2071 	struct nd_addrlist	*result;
2072 	int			num;
2073 	struct netbuf		*na;
2074 	struct sockaddr_in	*sinbuf, *sin;
2075 	struct sockaddr_in6	*sin6buf, *sin6;
2076 	struct in_addr		**inaddr, **inaddrlist;
2077 	struct in6_addr		**in6addr, **in6addrlist;
2078 
2079 	/* Address count */
2080 	num = 0;
2081 	if (af == AF_INET6) {
2082 		in6addrlist = (struct in6_addr **)haddrlist;
2083 
2084 		/*
2085 		 * Exclude IPv4-mapped IPv6 addresses from the count, as
2086 		 * these are not included in the nd_addrlist we return.
2087 		 */
2088 		for (in6addr = in6addrlist; *in6addr != NULL; in6addr++)
2089 			if (!IN6_IS_ADDR_V4MAPPED(*in6addr))
2090 				num++;
2091 	} else {
2092 		inaddrlist = (struct in_addr **)haddrlist;
2093 
2094 		for (inaddr = inaddrlist; *inaddr != NULL; inaddr++)
2095 			num++;
2096 	}
2097 	if (num == 0)
2098 		return (ND_NOHOST);
2099 
2100 	result = malloc(sizeof (struct nd_addrlist));
2101 	if (result == 0)
2102 		return (ND_NOMEM);
2103 
2104 	result->n_cnt = num;
2105 	result->n_addrs = calloc(num, sizeof (struct netbuf));
2106 	if (result->n_addrs == 0) {
2107 		free(result);
2108 		return (ND_NOMEM);
2109 	}
2110 
2111 	na = result->n_addrs;
2112 	if (af == AF_INET) {
2113 		sinbuf = calloc(num, sizeof (struct sockaddr_in));
2114 		if (sinbuf == NULL) {
2115 			free(result->n_addrs);
2116 			free(result);
2117 			return (ND_NOMEM);
2118 		}
2119 
2120 		sin = sinbuf;
2121 		for (inaddr = inaddrlist; *inaddr != NULL; inaddr++) {
2122 			na->len = na->maxlen = sizeof (struct sockaddr_in);
2123 			na->buf = (char *)sin;
2124 			sin->sin_family = AF_INET;
2125 			sin->sin_addr = **inaddr;
2126 			sin->sin_port = *servp;
2127 			na++;
2128 			sin++;
2129 		}
2130 	} else if (af == AF_INET6) {
2131 		sin6buf = calloc(num, sizeof (struct sockaddr_in6));
2132 		if (sin6buf == NULL) {
2133 			free(result->n_addrs);
2134 			free(result);
2135 			return (ND_NOMEM);
2136 		}
2137 
2138 		sin6 = sin6buf;
2139 		for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
2140 			if (IN6_IS_ADDR_V4MAPPED(*in6addr))
2141 				continue;
2142 
2143 			na->len = na->maxlen = sizeof (struct sockaddr_in6);
2144 			na->buf = (char *)sin6;
2145 			sin6->sin6_family = AF_INET6;
2146 			sin6->sin6_addr = **in6addr;
2147 			sin6->sin6_port = *servp;
2148 			na++;
2149 			sin6++;
2150 		}
2151 	}
2152 	*(nd_alist) = result;
2153 	return (ND_OK);
2154 }
2155 
2156 /*
2157  * Given a hostent and a servent, mallocs and populates
2158  * a new nd_hostservlist with host and service names.
2159  *
2160  * We could be passed in a NULL servent, in which case stringify port.
2161  */
2162 int
2163 hsents2ndhostservs(struct hostent *he, struct servent *se,
2164     ushort_t port, struct nd_hostservlist **hslist)
2165 {
2166 	struct	nd_hostservlist *result;
2167 	struct	nd_hostserv *hs;
2168 	int	hosts, servs, i, j;
2169 	char	**hn, **sn;
2170 
2171 	if ((result = malloc(sizeof (struct nd_hostservlist))) == 0)
2172 		return (ND_NOMEM);
2173 
2174 	/*
2175 	 * We initialize the counters to 1 rather than zero because
2176 	 * we have to count the "official" name as well as the aliases.
2177 	 */
2178 	for (hn = he->h_aliases, hosts = 1; hn && *hn; hn++, hosts++);
2179 	if (se)
2180 		for (sn = se->s_aliases, servs = 1; sn && *sn; sn++, servs++);
2181 	else
2182 		servs = 1;
2183 
2184 	if ((hs = calloc(hosts * servs, sizeof (struct nd_hostserv))) == 0) {
2185 		free(result);
2186 		return (ND_NOMEM);
2187 	}
2188 
2189 	result->h_cnt	= servs * hosts;
2190 	result->h_hostservs = hs;
2191 
2192 	for (i = 0, hn = he->h_aliases; i < hosts; i++) {
2193 		sn = se ? se->s_aliases : NULL;
2194 
2195 		for (j = 0; j < servs; j++) {
2196 			if (i == 0)
2197 				hs->h_host = strdup(he->h_name);
2198 			else
2199 				hs->h_host = strdup(*hn);
2200 			if (j == 0) {
2201 				if (se)
2202 					hs->h_serv = strdup(se->s_name);
2203 				else {
2204 					/* Convert to a number string */
2205 					char stmp[16];
2206 
2207 					(void) sprintf(stmp, "%d", port);
2208 					hs->h_serv = strdup(stmp);
2209 				}
2210 			} else
2211 				hs->h_serv = strdup(*sn++);
2212 
2213 			if ((hs->h_host == 0) || (hs->h_serv == 0)) {
2214 				free(result->h_hostservs);
2215 				free(result);
2216 				return (ND_NOMEM);
2217 			}
2218 			hs++;
2219 		}
2220 		if (i)
2221 			hn++;
2222 	}
2223 	*(hslist) = result;
2224 	return (ND_OK);
2225 }
2226 
2227 /*
2228  * Process results from nd_addrlist ( returned by netdir_getbyname)
2229  * into a hostent using buf.
2230  * *** ASSUMES that nd_addrlist->n_addrs->buf contains IP addresses in
2231  * sockaddr_in's ***
2232  */
2233 int
2234 ndaddr2hent(int af, const char *nam, struct nd_addrlist *addrs,
2235     struct hostent *result, char *buffer, int buflen)
2236 {
2237 	int	i, count;
2238 	struct	in_addr *addrp;
2239 	struct	in6_addr *addr6p;
2240 	char	**addrvec;
2241 	struct	netbuf *na;
2242 	size_t	len;
2243 
2244 	result->h_name		= buffer;
2245 	result->h_addrtype	= af;
2246 	result->h_length	= (af == AF_INET) ? sizeof (*addrp):
2247 						    sizeof (*addr6p);
2248 
2249 	/*
2250 	 * Build addrlist at start of buffer (after name);  store the
2251 	 * addresses themselves at the end of the buffer.
2252 	 */
2253 	len = strlen(nam) + 1;
2254 	addrvec = (char **)ROUND_UP(buffer + len, sizeof (*addrvec));
2255 	result->h_addr_list 	= addrvec;
2256 
2257 	if (af == AF_INET) {
2258 		addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
2259 		    sizeof (*addrp));
2260 
2261 		count = addrs->n_cnt;
2262 		if ((char *)(&addrvec[count + 1]) > (char *)(&addrp[-count]))
2263 			return (ND_NOMEM);
2264 
2265 		(void) memcpy(buffer, nam, len);
2266 
2267 		for (na = addrs->n_addrs, i = 0;  i < count;  na++, i++) {
2268 			--addrp;
2269 			(void) memcpy(addrp,
2270 			    /* LINTED pointer cast */
2271 			    &((struct sockaddr_in *)na->buf)->sin_addr,
2272 			    sizeof (*addrp));
2273 			*addrvec++ = (char *)addrp;
2274 		}
2275 	} else {
2276 		addr6p = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
2277 			sizeof (*addr6p));
2278 
2279 		count = addrs->n_cnt;
2280 		if ((char *)(&addrvec[count + 1]) > (char *)(&addr6p[-count]))
2281 			return (ND_NOMEM);
2282 
2283 		(void) memcpy(buffer, nam, len);
2284 
2285 		for (na = addrs->n_addrs, i = 0;  i < count;  na++, i++) {
2286 			--addr6p;
2287 			(void) memcpy(addr6p,
2288 			    /* LINTED pointer cast */
2289 			    &((struct sockaddr_in6 *)na->buf)->sin6_addr,
2290 			    sizeof (*addr6p));
2291 			*addrvec++ = (char *)addr6p;
2292 		}
2293 	}
2294 	*addrvec = 0;
2295 	result->h_aliases = addrvec;
2296 
2297 	return (ND_OK);
2298 }
2299 
2300 /*
2301  * Process results from nd_addrlist ( returned by netdir_getbyname)
2302  * into a servent using buf.
2303  */
2304 int
2305 ndaddr2srent(const char *name, const char *proto, ushort_t port,
2306     struct servent *result, char *buffer, int buflen)
2307 {
2308 	size_t	i;
2309 	char	*bufend = (buffer + buflen);
2310 
2311 	result->s_port = (int)port;
2312 
2313 	result->s_aliases =
2314 	    (char **)ROUND_UP(buffer, sizeof (char *));
2315 	result->s_aliases[0] = NULL;
2316 	buffer = (char *)&result->s_aliases[1];
2317 	result->s_name = buffer;
2318 	i = strlen(name) + 1;
2319 	if ((buffer + i) > bufend)
2320 		return (ND_NOMEM);
2321 	(void) memcpy(buffer, name, i);
2322 	buffer += i;
2323 
2324 	result->s_proto	= buffer;
2325 	i = strlen(proto) + 1;
2326 	if ((buffer + i) > bufend)
2327 		return (ND_NOMEM);
2328 	(void) memcpy(buffer, proto, i);
2329 	buffer += i;
2330 
2331 	return (ND_OK);
2332 }
2333 
2334 /*
2335  * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
2336  * into a hostent using buf.
2337  * *** ASSUMES that nd_buf->buf is a sockaddr_in ***
2338  */
2339 int
2340 ndhostserv2hent(struct netbuf *nbuf, struct nd_hostservlist *addrs,
2341     struct hostent *result, char *buffer, int buflen)
2342 {
2343 	int	i, count;
2344 	char	*aliasp;
2345 	char	**aliasvec;
2346 	struct	sockaddr_in *sa;
2347 	struct	nd_hostserv *hs;
2348 	const	char *la;
2349 	size_t	length;
2350 
2351 	/* First, give the lonely address a specious home in h_addr_list. */
2352 	aliasp   = (char  *)ROUND_UP(buffer, sizeof (sa->sin_addr));
2353 	/* LINTED pointer cast */
2354 	sa = (struct sockaddr_in *)nbuf->buf;
2355 	(void) memcpy(aliasp, &(sa->sin_addr), sizeof (sa->sin_addr));
2356 	aliasvec = (char **)ROUND_UP(aliasp + sizeof (sa->sin_addr),
2357 		sizeof (*aliasvec));
2358 	result->h_addr_list = aliasvec;
2359 	*aliasvec++ = aliasp;
2360 	*aliasvec++ = 0;
2361 
2362 	/*
2363 	 * Build h_aliases at start of buffer (after addr and h_addr_list);
2364 	 * store the alias strings at the end of the buffer (before h_name).
2365 	 */
2366 
2367 	aliasp = buffer + buflen;
2368 
2369 	result->h_aliases	= aliasvec;
2370 
2371 	hs = addrs->h_hostservs;
2372 	if (!hs)
2373 		return (ND_NOHOST);
2374 
2375 	length = strlen(hs->h_host) + 1;
2376 	aliasp -= length;
2377 	if ((char *)(&aliasvec[1]) > aliasp)
2378 		return (ND_NOMEM);
2379 	(void) memcpy(aliasp, hs->h_host, length);
2380 
2381 	result->h_name		= aliasp;
2382 	result->h_addrtype	= AF_INET;
2383 	result->h_length	= sizeof (sa->sin_addr);
2384 
2385 	/*
2386 	 * Assumption: the netdir nametoaddr_libs
2387 	 * sort the vector of (host, serv) pairs in such a way that
2388 	 * all pairs with the same host name are contiguous.
2389 	 */
2390 	la = hs->h_host;
2391 	count = addrs->h_cnt;
2392 	for (i = 0;  i < count;  i++, hs++)
2393 		if (strcmp(la, hs->h_host) != 0) {
2394 			size_t len = strlen(hs->h_host) + 1;
2395 
2396 			aliasp -= len;
2397 			if ((char *)(&aliasvec[2]) > aliasp)
2398 				return (ND_NOMEM);
2399 			(void) memcpy(aliasp, hs->h_host, len);
2400 			*aliasvec++ = aliasp;
2401 			la = hs->h_host;
2402 		}
2403 	*aliasvec = 0;
2404 
2405 	return (ND_OK);
2406 }
2407 
2408 /*
2409  * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
2410  * into a servent using buf.
2411  */
2412 int
2413 ndhostserv2srent(int port, const char *proto, struct nd_hostservlist *addrs,
2414     struct servent *result, char *buffer, int buflen)
2415 {
2416 	int	i, count;
2417 	char	*aliasp;
2418 	char	**aliasvec;
2419 	struct	nd_hostserv *hs;
2420 	const	char *host_cname;
2421 	size_t	leni, lenj;
2422 
2423 	result->s_port = port;
2424 	/*
2425 	 * Build s_aliases at start of buffer;
2426 	 * store proto and aliases at the end of the buffer (before h_name).
2427 	 */
2428 
2429 	aliasp = buffer + buflen;
2430 	aliasvec = (char **)ROUND_UP(buffer, sizeof (char *));
2431 
2432 	result->s_aliases	= aliasvec;
2433 
2434 	hs = addrs->h_hostservs;
2435 	if (!hs)
2436 		return (ND_NOHOST);
2437 	host_cname = hs->h_host;
2438 
2439 	leni = strlen(proto) + 1;
2440 	lenj = strlen(hs->h_serv) + 1;
2441 	if ((char *)(&aliasvec[2]) > (aliasp - leni - lenj))
2442 		return (ND_NOMEM);
2443 
2444 	aliasp -= leni;
2445 	(void) memcpy(aliasp, proto, leni);
2446 	result->s_proto = aliasp;
2447 
2448 	aliasp -= lenj;
2449 	(void) memcpy(aliasp, hs->h_serv, lenj);
2450 	result->s_name = aliasp;
2451 
2452 	/*
2453 	 * Assumption: the netdir nametoaddr_libs
2454 	 * do a host aliases first and serv aliases next
2455 	 * enumeration for creating the list of hostserv
2456 	 * structures.
2457 	 */
2458 	count = addrs->h_cnt;
2459 	for (i = 0;
2460 	    i < count && hs->h_serv && strcmp(hs->h_host, host_cname) == 0;
2461 	    i++, hs++) {
2462 		size_t len = strlen(hs->h_serv) + 1;
2463 
2464 		aliasp -= len;
2465 		if ((char *)(&aliasvec[2]) > aliasp)
2466 			return (ND_NOMEM);
2467 		(void) memcpy(aliasp, hs->h_serv, len);
2468 		*aliasvec++ = aliasp;
2469 	}
2470 	*aliasvec = NULL;
2471 
2472 	return (ND_OK);
2473 }
2474 
2475 
2476 static int
2477 nd2herrno(int nerr)
2478 {
2479 	switch (nerr) {
2480 	case ND_OK:
2481 		return (0);
2482 	case ND_TRY_AGAIN:
2483 		return (TRY_AGAIN);
2484 	case ND_NO_RECOVERY:
2485 	case ND_BADARG:
2486 	case ND_NOMEM:
2487 		return (NO_RECOVERY);
2488 	case ND_NO_DATA:
2489 		return (NO_DATA);
2490 	case ND_NOHOST:
2491 	case ND_NOSERV:
2492 		return (HOST_NOT_FOUND);
2493 	default:
2494 		return (NO_RECOVERY);
2495 	}
2496 }
2497 
2498 /*
2499  * This is a utility function so that various parts of libnsl can
2500  * easily send ioctls down to ip.
2501  *
2502  */
2503 int
2504 nss_ioctl(int af, int cmd, void *arg)
2505 {
2506 	int	fd;
2507 	char	*devpath;
2508 	int	retv;
2509 
2510 	switch (af) {
2511 	case AF_INET6:
2512 		devpath = UDP6DEV;
2513 		break;
2514 	case AF_INET:
2515 	case AF_UNSPEC:
2516 	default:
2517 		devpath = UDPDEV;
2518 	}
2519 	if ((fd = open(devpath, O_RDONLY)) < 0) {
2520 		return (-1);
2521 	}
2522 	while ((retv = ioctl(fd, cmd, arg)) == -1) {
2523 		if (errno != EINTR)
2524 	break;
2525 	}
2526 	(void) close(fd);
2527 	return (retv);
2528 }
2529 
2530 static int
2531 nss_strioctl(int af, int cmd, void *ptr, int ilen)
2532 {
2533 	struct strioctl str;
2534 
2535 	str.ic_cmd = cmd;
2536 	str.ic_timout = 0;
2537 	str.ic_len = ilen;
2538 	str.ic_dp = ptr;
2539 
2540 	return (nss_ioctl(af, I_STR, &str));
2541 }
2542 
2543 static struct ifinfo *
2544 get_local_info(void)
2545 {
2546 	int	numifs;
2547 	int	n;
2548 	char	*buf = NULL;
2549 	size_t	needed;
2550 	struct lifconf	lifc;
2551 	struct lifreq	lifreq, *lifr;
2552 	struct lifnum	lifn;
2553 	struct ifinfo	*localinfo;
2554 
2555 	lifn.lifn_family = AF_UNSPEC;
2556 	lifn.lifn_flags = 0;
2557 
2558 getifnum:
2559 	if (nss_ioctl(AF_UNSPEC, SIOCGLIFNUM, &lifn) == -1) {
2560 		numifs = MAXIFS;
2561 	} else {
2562 		numifs = lifn.lifn_count;
2563 	}
2564 
2565 	/*
2566 	 * Add a small fudge factor in case interfaces get plumbed between
2567 	 * the call to SIOCGLIFNUM and SIOCGLIFCONF.
2568 	 */
2569 	needed = (numifs + 4) * sizeof (lifreq);
2570 	if (buf == NULL)
2571 		buf = malloc(needed);
2572 	else
2573 		buf = realloc(buf, needed);
2574 	if (buf == NULL) {
2575 		(void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
2576 		_nderror = ND_NOMEM;
2577 		return (NULL);
2578 	}
2579 	lifc.lifc_family = AF_UNSPEC;
2580 	lifc.lifc_flags = 0;
2581 	lifc.lifc_len = needed;
2582 	lifc.lifc_buf = buf;
2583 	if (nss_ioctl(AF_UNSPEC, SIOCGLIFCONF, &lifc) == -1) {
2584 		/*
2585 		 * IP returns EINVAL if the buffer was too small to fit
2586 		 * all of the entries.  If that's the case, go back and
2587 		 * try again.
2588 		 */
2589 		if (errno == EINVAL)
2590 			goto getifnum;
2591 
2592 		(void) syslog(LOG_ERR, "n2a get_local_info: "
2593 		    "ioctl (get interface configuration): %m");
2594 		free(buf);
2595 		_nderror = ND_SYSTEM;
2596 		return (NULL);
2597 	}
2598 	/* LINTED pointer cast */
2599 	lifr = (struct lifreq *)buf;
2600 	numifs = lifc.lifc_len/sizeof (lifreq);
2601 	localinfo = malloc(ifinfosize(numifs));
2602 	if (localinfo == NULL) {
2603 		(void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
2604 		free(buf);
2605 		_nderror = ND_SYSTEM;
2606 		return (NULL);
2607 	}
2608 
2609 	/* LINTED pointer cast */
2610 	localinfo->addresses = (struct __ifaddr *)
2611 	    ((char *)localinfo + sizeof (struct ifinfo));
2612 
2613 	for (localinfo->count = 0, n = numifs; n > 0; n--, lifr++) {
2614 		int af;
2615 
2616 		lifreq = *lifr;
2617 		af = lifreq.lifr_addr.ss_family;
2618 
2619 		/* Squirrel away the address */
2620 		if (ifassign(lifreq, localinfo->count, IF_ADDR) == 0)
2621 			continue;
2622 
2623 		if (nss_ioctl(af, SIOCGLIFFLAGS, &lifreq) < 0) {
2624 			(void) syslog(LOG_ERR,
2625 			    "n2a get_local_info: "
2626 			    "ioctl (get interface flags): %m");
2627 			continue;
2628 		}
2629 		if (!(lifreq.lifr_flags & IFF_UP))
2630 			continue;
2631 
2632 		if (nss_ioctl(af, SIOCGLIFNETMASK, &lifreq) < 0) {
2633 			(void) syslog(LOG_ERR,
2634 			    "n2a get_local_info: "
2635 			    "ioctl (get interface netmask): %m");
2636 			continue;
2637 		}
2638 
2639 		if (ifassign(lifreq, localinfo->count, IF_MASK) == 0)
2640 			continue;
2641 
2642 		localinfo->count++;
2643 	}
2644 
2645 	free(buf);
2646 	return (localinfo);
2647 }
2648 
2649 static int
2650 __inet_ifassign(sa_family_t af, struct __ifaddr *ifa, __ifaddr_type type,
2651     void *addr) {
2652 	switch (type) {
2653 	case IF_ADDR:
2654 		ifa->af = af;
2655 		if (af == AF_INET6) {
2656 			ifa->addr.in6 = *(struct in6_addr *)addr;
2657 		} else {
2658 			ifa->addr.in4 = *(struct in_addr *)addr;
2659 		}
2660 		break;
2661 	case IF_MASK:
2662 		if (ifa->af == af) {
2663 			if (af == AF_INET6) {
2664 				ifa->mask.in6 = *(struct in6_addr *)addr;
2665 			} else {
2666 				ifa->mask.in4 = *(struct in_addr *)addr;
2667 			}
2668 		} else {
2669 			return (0);
2670 		}
2671 		break;
2672 	default:
2673 		return (0);
2674 	}
2675 
2676 	return (1);
2677 }
2678 
2679 /*
2680  *  Some higher-level routines for determining if an address is
2681  *  on a local network.
2682  *
2683  *      __inet_get_local_interfaces() - get an opaque handle with
2684  *          with a list of local interfaces
2685  *      __inet_address_is_local() - return 1 if an address is
2686  *          on a local network; 0 otherwise
2687  *      __inet_free_local_interfaces() - free handle that was
2688  *          returned by __inet_get_local_interfaces()
2689  *
2690  *  A typical calling sequence is:
2691  *
2692  *      p = __inet_get_local_interfaces();
2693  *      if (__inet_address_is_local(p, inaddr)) {
2694  *          ...
2695  *      }
2696  *      __inet_free_local_interfaces(p);
2697  */
2698 
2699 /*
2700  *  Return an opaque pointer to a list of configured interfaces.
2701  */
2702 void *
2703 __inet_get_local_interfaces(void)
2704 {
2705 	return (get_local_info());
2706 }
2707 
2708 /*
2709  *  Free memory allocated by inet_local_interfaces().
2710  */
2711 void
2712 __inet_free_local_interfaces(void *p)
2713 {
2714 	free(p);
2715 }
2716 
2717 /*
2718  *  Determine if an address is on a local network.
2719  *
2720  *  Might have made sense to use SIOCTONLINK, except that it doesn't
2721  *  handle matching on IPv4 network addresses.
2722  */
2723 int
2724 __inet_address_is_local_af(void *p, sa_family_t af, void *addr) {
2725 
2726 	struct ifinfo	*localinfo = (struct ifinfo *)p;
2727 	int		i, a;
2728 	struct in_addr	v4addr;
2729 
2730 	if (localinfo == 0)
2731 		return (0);
2732 
2733 	if (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr)) {
2734 		IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addr, &v4addr);
2735 		af = AF_INET;
2736 		addr = (void *)&v4addr;
2737 	}
2738 
2739 	for (i = 0; i < localinfo->count; i++) {
2740 		if (ifaf(i) == af) {
2741 			if (af == AF_INET6) {
2742 				struct in6_addr *a6 = (struct in6_addr *)addr;
2743 				for (a = 0; a < sizeof (a6->s6_addr); a++) {
2744 					if ((a6->s6_addr[a] &
2745 						ifmask6(i).s6_addr[a]) !=
2746 						(ifaddr6(i).s6_addr[a] &
2747 						ifmask6(i).s6_addr[a]))
2748 						break;
2749 				}
2750 				if (a >= sizeof (a6->s6_addr))
2751 					return (1);
2752 			} else {
2753 				if ((((struct in_addr *)addr)->s_addr &
2754 						ifmask4(i).s_addr) ==
2755 					(ifaddr4(i).s_addr &
2756 						ifmask4(i).s_addr))
2757 					return (1);
2758 			}
2759 		}
2760 	}
2761 
2762 	return (0);
2763 }
2764 
2765 int
2766 __inet_address_is_local(void *p, struct in_addr addr)
2767 {
2768 	return (__inet_address_is_local_af(p, AF_INET, &addr));
2769 }
2770 
2771 int
2772 __inet_uaddr_is_local(void *p, struct netconfig *nc, char *uaddr)
2773 {
2774 	struct netbuf		*taddr;
2775 	sa_family_t		af;
2776 	int			ret;
2777 
2778 	taddr = uaddr2taddr(nc, uaddr);
2779 	if (taddr == 0)
2780 		return (0);
2781 
2782 	/* LINTED pointer cast */
2783 	af = ((struct sockaddr *)taddr->buf)->sa_family;
2784 
2785 	ret = __inet_address_is_local_af(p, af,
2786 		(af == AF_INET6) ?
2787 		/* LINTED pointer cast */
2788 		(void *)&((struct sockaddr_in6 *)taddr->buf)->sin6_addr :
2789 		/* LINTED pointer cast */
2790 		(void *)&((struct sockaddr_in *)taddr->buf)->sin_addr);
2791 
2792 	netdir_free(taddr, ND_ADDR);
2793 	return (ret);
2794 }
2795 
2796 
2797 int
2798 __inet_address_count(void *p)
2799 {
2800 	struct ifinfo *lp = (struct ifinfo *)p;
2801 
2802 	if (lp != 0) {
2803 		return (lp->count);
2804 	} else {
2805 		return (0);
2806 	}
2807 }
2808 
2809 uint32_t
2810 __inet_get_addr(void *p, int n)
2811 {
2812 	struct ifinfo *localinfo = (struct ifinfo *)p;
2813 
2814 	if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
2815 		return (0);
2816 
2817 	return (ifaddr4(n).s_addr);
2818 }
2819 
2820 uint32_t
2821 __inet_get_network(void *p, int n)
2822 {
2823 	struct ifinfo *localinfo = (struct ifinfo *)p;
2824 
2825 	if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
2826 		return (0);
2827 
2828 	return (ifaddr4(n).s_addr & ifmask4(n).s_addr);
2829 }
2830 
2831 char *
2832 __inet_get_uaddr(void *p, struct netconfig *nc, int n)
2833 {
2834 	struct ifinfo *localinfo = (struct ifinfo *)p;
2835 	char *uaddr;
2836 	struct sockaddr_in sin4;
2837 	struct sockaddr_in6 sin6;
2838 	struct netbuf nb;
2839 
2840 	if (localinfo == 0 || nc == 0 || n >= localinfo->count)
2841 		return (0);
2842 
2843 	if (ifaf(n) == AF_INET6) {
2844 		if (strcmp(NC_INET6, nc->nc_protofmly) != 0)
2845 			return (0);
2846 		(void) memset(&sin6, 0, sizeof (sin6));
2847 		sin6.sin6_family = AF_INET6;
2848 		sin6.sin6_addr = ifaddr6(n);
2849 		nb.buf = (char *)&sin6;
2850 		nb.len = sizeof (sin6);
2851 	} else {
2852 		if (strcmp(NC_INET, nc->nc_protofmly) != 0)
2853 			return (0);
2854 		(void) memset(&sin4, 0, sizeof (sin4));
2855 		sin4.sin_family = AF_INET;
2856 		sin4.sin_addr = ifaddr4(n);
2857 		nb.buf = (char *)&sin4;
2858 		nb.len = sizeof (sin4);
2859 	}
2860 
2861 	nb.maxlen = nb.len;
2862 
2863 	uaddr = taddr2uaddr(nc, &nb);
2864 	return (uaddr);
2865 }
2866 
2867 char *
2868 __inet_get_networka(void *p, int n)
2869 {
2870 	struct ifinfo	*localinfo = (struct ifinfo *)p;
2871 
2872 	if (localinfo == 0 || n >= localinfo->count)
2873 		return (0);
2874 
2875 	if (ifaf(n) == AF_INET6) {
2876 		char		buf[INET6_ADDRSTRLEN];
2877 		struct in6_addr	in6;
2878 		int		i;
2879 
2880 		for (i = 0; i < sizeof (in6.s6_addr); i++) {
2881 			in6.s6_addr[i] = ifaddr6(n).s6_addr[i] &
2882 					ifmask6(n).s6_addr[i];
2883 		}
2884 		return (strdup(inet_ntop(AF_INET6, &in6, buf, sizeof (buf))));
2885 	} else {
2886 		struct in_addr	in4;
2887 
2888 		in4.s_addr = ifaddr4(n).s_addr & ifmask4(n).s_addr;
2889 		return (strdup(inet_ntoa(in4)));
2890 	}
2891 }
2892 
2893 static int
2894 in_list(struct in_addr *addrs, int n, struct in_addr a)
2895 {
2896 	int i;
2897 
2898 	for (i = 0; i < n; i++) {
2899 		if (addrs[i].s_addr == a.s_addr)
2900 			return (1);
2901 	}
2902 	return (0);
2903 }
2904 
2905 static int
2906 getbroadcastnets(struct netconfig *tp, struct in_addr **addrs)
2907 {
2908 	struct ifconf ifc;
2909 	struct ifreq ifreq, *ifr;
2910 	struct sockaddr_in *sin;
2911 	struct in_addr a;
2912 	int fd;
2913 	int n, i, numifs;
2914 	char *buf;
2915 	int	use_loopback = 0;
2916 
2917 	_nderror = ND_SYSTEM;
2918 	fd = open(tp->nc_device, O_RDONLY);
2919 	if (fd < 0) {
2920 		(void) syslog(LOG_ERR,
2921 	    "broadcast: open to get interface configuration: %m");
2922 		return (0);
2923 	}
2924 	if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0)
2925 		numifs = MAXIFS;
2926 	buf = malloc(numifs * sizeof (struct ifreq));
2927 	if (buf == NULL) {
2928 		(void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
2929 		(void) close(fd);
2930 		return (0);
2931 	}
2932 	*addrs = malloc(numifs * sizeof (struct in_addr));
2933 	if (*addrs == NULL) {
2934 		(void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
2935 		free(buf);
2936 		(void) close(fd);
2937 		return (0);
2938 	}
2939 	ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
2940 	ifc.ifc_buf = buf;
2941 	/*
2942 	 * Ideally, this ioctl should also tell me, how many bytes were
2943 	 * finally allocated, but it doesnt.
2944 	 */
2945 	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
2946 		(void) syslog(LOG_ERR,
2947 	    "broadcast: ioctl (get interface configuration): %m");
2948 		free(buf);
2949 		free(*addrs);
2950 		(void) close(fd);
2951 		return (0);
2952 	}
2953 
2954 retry:
2955 	/* LINTED pointer cast */
2956 	ifr = (struct ifreq *)buf;
2957 	for (i = 0, n = ifc.ifc_len / (int)sizeof (struct ifreq);
2958 		n > 0; n--, ifr++) {
2959 		ifreq = *ifr;
2960 		if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
2961 			(void) syslog(LOG_ERR,
2962 		    "broadcast: ioctl (get interface flags): %m");
2963 			continue;
2964 		}
2965 		if (!(ifreq.ifr_flags & IFF_UP) ||
2966 		    (ifr->ifr_addr.sa_family != AF_INET))
2967 			continue;
2968 		if (ifreq.ifr_flags & IFF_BROADCAST) {
2969 			/* LINTED pointer cast */
2970 			sin = (struct sockaddr_in *)&ifr->ifr_addr;
2971 			if (ioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
2972 				/* May not work with other implementation */
2973 				a = _inet_makeaddr(
2974 				    inet_netof(sin->sin_addr),
2975 				    INADDR_ANY);
2976 				if (!in_list(*addrs, i, a))
2977 					(*addrs)[i++] = a;
2978 			} else {
2979 				/* LINTED pointer cast */
2980 				a = ((struct sockaddr_in *)
2981 				    &ifreq.ifr_addr)->sin_addr;
2982 				if (!in_list(*addrs, i, a))
2983 					(*addrs)[i++] = a;
2984 			}
2985 			continue;
2986 		}
2987 		if (use_loopback && (ifreq.ifr_flags & IFF_LOOPBACK)) {
2988 			/* LINTED pointer cast */
2989 			sin = (struct sockaddr_in *)&ifr->ifr_addr;
2990 			a = sin->sin_addr;
2991 			if (!in_list(*addrs, i, a))
2992 				(*addrs)[i++] = a;
2993 			continue;
2994 		}
2995 		if (ifreq.ifr_flags & IFF_POINTOPOINT) {
2996 			if (ioctl(fd, SIOCGIFDSTADDR, (char *)&ifreq) < 0)
2997 				continue;
2998 			/* LINTED pointer cast */
2999 			a = ((struct sockaddr_in *)
3000 			    &ifreq.ifr_addr)->sin_addr;
3001 			if (!in_list(*addrs, i, a))
3002 				(*addrs)[i++] = a;
3003 			continue;
3004 		}
3005 	}
3006 	if (i == 0 && !use_loopback) {
3007 		use_loopback = 1;
3008 		goto retry;
3009 	}
3010 	free(buf);
3011 	(void) close(fd);
3012 	if (i)
3013 		_nderror = ND_OK;
3014 	else
3015 		free(*addrs);
3016 	return (i);
3017 }
3018 
3019 /*
3020  * This is lifted straight from libsocket/inet/inet_mkaddr.c.
3021  * Copied here to avoid our dependency on libsocket. More importantly,
3022  * to make sure partially static apps that use libnsl, but not
3023  * libsocket, don't get screwed up.
3024  * If you understand the above paragraph, try to get rid of
3025  * this copy of inet_makeaddr; if you don;t, leave it alone.
3026  *
3027  * Formulate an Internet address from network + host.  Used in
3028  * building addresses stored in the ifnet structure.
3029  */
3030 static struct in_addr
3031 _inet_makeaddr(in_addr_t net, in_addr_t host)
3032 {
3033 	in_addr_t addr;
3034 	struct in_addr inaddr;
3035 
3036 	if (net < 128)
3037 		addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST);
3038 	else if (net < 65536)
3039 		addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST);
3040 	else if (net < 16777216L)
3041 		addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST);
3042 	else
3043 		addr = net | host;
3044 	inaddr.s_addr = htonl(addr);
3045 	return (inaddr);
3046 }
3047 
3048 /*
3049  * Routine to read the default configuration file and check if SORT_ADDRS
3050  * is set to NO or FALSE. This routine is called by order_haddrlist_af()
3051  * to determine if the addresses need to be sorted.
3052  */
3053 static boolean_t
3054 _read_nsw_file(void)
3055 {
3056 	char	defval[LINESIZE];
3057 	FILE	*defl;
3058 	boolean_t	nosort = B_FALSE;
3059 
3060 
3061 	do {
3062 		defl = fopen(__NSW_DEFAULT_FILE, "rF");
3063 	} while ((defl == NULL) && (errno == EINTR));
3064 
3065 	if (defl == NULL)
3066 		return (B_FALSE);
3067 
3068 	while (fgets(defval, sizeof (defval), defl) != NULL) {
3069 		if ((strncmp(DONT_SORT, defval, sizeof (DONT_SORT) - 1) == 0) ||
3070 		    (strncmp(DONT_SORT2, defval,
3071 			sizeof (DONT_SORT2) - 1) == 0)) {
3072 			nosort = B_TRUE;
3073 			break;
3074 		}
3075 	}
3076 	(void) fclose(defl);
3077 	return (nosort);
3078 }
3079