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