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