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