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 */
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
164static int
165inetdir_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 */
184int
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 */
814int
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
1227static DEFINE_NSS_DB_ROOT(db_root_hosts);
1228static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
1229static 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 */
1239int
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
1259nss_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
1277static 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
1304struct 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
1324struct 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
1346struct 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
1366struct 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
1386static 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
1393struct 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
1410struct 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 */
1437int
1438str2servent(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
1545union __v4v6addr {
1546	struct in6_addr	in6;
1547	struct in_addr	in4;
1548};
1549
1550struct __ifaddr {
1551	sa_family_t		af;
1552	union __v4v6addr	addr;
1553	union __v4v6addr	mask;
1554};
1555
1556struct ifinfo {
1557	int		count;
1558	struct __ifaddr	*addresses;
1559};
1560
1561typedef enum {ADDR_ONLINK = 0, ADDR_OFFLINK} addr_class_t;
1562#define	ADDR_NUMCLASSES	2
1563
1564typedef enum {IF_ADDR, IF_MASK}	__ifaddr_type;
1565static int	__inet_ifassign(sa_family_t, struct __ifaddr *, __ifaddr_type,
1566				void *);
1567int		__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 */
1601void
1602order_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 */
1662static void
1663order_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 */
1784static void
1785order_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 */
1824static uint_t
1825ip_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 */
1881typedef	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. */
1896static int
1897rule_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. */
1907static int
1908rule_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. */
1923static int
1924rule_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. */
1938static int
1939rule_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. */
1949static int
1950rule_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. */
1960static int
1961rule_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 */
1971static int
1972rule_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. */
1991static int
1992rule_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 */
2005static int
2006rule_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 */
2037static int
2038dstcmp(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 */
2071int
2072hent2ndaddr(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 */
2165int
2166hsents2ndhostservs(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 */
2237int
2238ndaddr2hent(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 */
2308int
2309ndaddr2srent(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 */
2343int
2344ndhostserv2hent(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 */
2416int
2417ndhostserv2srent(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
2480static int
2481nd2herrno(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 */
2507int
2508nss_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
2534static int
2535nss_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
2547static struct ifinfo *
2548get_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
2562getifnum:
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
2653static 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 */
2706void *
2707__inet_get_local_interfaces(void)
2708{
2709	return (get_local_info());
2710}
2711
2712/*
2713 *  Free memory allocated by inet_local_interfaces().
2714 */
2715void
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 */
2727int
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
2769int
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
2775int
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
2800int
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
2812uint32_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
2823uint32_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
2834char *
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
2870char *
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
2896static int
2897in_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
2908static int
2909getbroadcastnets(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
2957retry:
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 */
3033static 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 */
3056static 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