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