root/usr/src/lib/libnsl/nss/netdir_inet.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2012 Milan Jurik. All rights reserved.
 */

/*
 * This is where we have chosen to combine every useful bit of code for
 * all the Solaris frontends to lookup hosts, services, and netdir information
 * for inet family (udp, tcp) transports. gethostbyYY(), getservbyYY(), and
 * netdir_getbyYY() are all implemented on top of this code. Similarly,
 * netdir_options, taddr2uaddr, and uaddr2taddr for inet transports also
 * find a home here.
 *
 * If the netconfig structure supplied has NO nametoaddr libs (i.e. a "-"
 * in /etc/netconfig), this code calls the name service switch, and
 * therefore, /etc/nsswitch.conf is effectively the only place that
 * dictates hosts/serv lookup policy.
 * If an administrator chooses to bypass the name service switch by
 * specifying third party supplied nametoaddr libs in /etc/netconfig, this
 * implementation does NOT call the name service switch, it merely loops
 * through the nametoaddr libs. In this case, if this code was called
 * from gethost/servbyYY() we marshal the inet specific struct into
 * transport independent netbuf or hostserv, and unmarshal the resulting
 * nd_addrlist or hostservlist back into hostent and servent, as the case
 * may be.
 *
 * Goes without saying that most of the future bugs in gethost/servbyYY
 * and netdir_getbyYY are lurking somewhere here.
 */

#include "mt.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/byteorder.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <thread.h>
#include <synch.h>
#include <sys/utsname.h>
#include <netdb.h>
#include <netconfig.h>
#include <netdir.h>
#include <tiuser.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <inet/ip.h>
#include <inet/ip6_asp.h>
#include <sys/dlpi.h>
#include <nss_dbdefs.h>
#include <nss_netdir.h>
#include <syslog.h>
#include <nsswitch.h>
#include "nss.h"

#define MAXIFS 32
#define UDPDEV  "/dev/udp"
#define UDP6DEV "/dev/udp6"

#define DOOR_GETHOSTBYNAME_R    _switch_gethostbyname_r
#define DOOR_GETHOSTBYADDR_R    _switch_gethostbyaddr_r
#define DOOR_GETIPNODEBYNAME_R  _switch_getipnodebyname_r
#define DOOR_GETIPNODEBYADDR_R  _switch_getipnodebyaddr_r

#define DONT_SORT       "SORT_ADDRS=NO"
#define DONT_SORT2      "SORT_ADDRS=FALSE"
#define LINESIZE        100

/*
 * constant values of addresses for HOST_SELF_BIND, HOST_SELF_CONNECT
 * and localhost.
 *
 * The following variables are static to the extent that they should
 * not be visible outside of this file.
 */
static char *localaddr[] = {"\000\000\000\000", NULL};
static char *connectaddr[] = {"\177\000\000\001", NULL};
static char *localaddr6[] =
{"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", NULL};
static char *connectaddr6[] =
{"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", NULL};

/* IPv4 nd_addrlist */
static mutex_t  nd_addr_lock = DEFAULTMUTEX;
static struct sockaddr_in sa_con;
static struct netbuf nd_conbuf = {sizeof (sa_con),\
    sizeof (sa_con), (char *)&sa_con};
static struct nd_addrlist nd_conaddrlist = {1, &nd_conbuf};

/* IPv6 nd_addrlist */
static mutex_t  nd6_addr_lock = DEFAULTMUTEX;
static struct sockaddr_in6 sa6_con;
static struct netbuf nd6_conbuf = {sizeof (sa6_con),\
        sizeof (sa6_con), (char *)&sa6_con};
static struct nd_addrlist nd6_conaddrlist = {1, &nd6_conbuf};

#define LOCALHOST "localhost"

struct servent *_switch_getservbyname_r(const char *, const char *,
    struct servent *, char *, int);
struct servent *_switch_getservbyport_r(int, const char *, struct servent *,
    char *, int);

static int __herrno2netdir(int h_errnop);
static struct ifinfo *get_local_info(void);
static int getbroadcastnets(struct netconfig *, struct in_addr **);
static int hent2ndaddr(int, char **, int *, struct nd_addrlist **);
static int ndaddr2hent(int, const char *, struct nd_addrlist *,
    struct hostent *, char *, int);
static int hsents2ndhostservs(struct hostent *, struct servent *, ushort_t,
    struct nd_hostservlist **);
static int ndaddr2srent(const char *, const char *, ushort_t, struct servent *,
    char *, int);
static int ndhostserv2hent(struct netbuf *, struct nd_hostservlist *,
    struct hostent *, char *, int);
static int ndhostserv2srent(int, const char *, struct nd_hostservlist *,
    struct servent *, char *, int);
static int nd2herrno(int nerr);
static void order_haddrlist_inet(char **haddrlist, size_t addrcount);
static void order_haddrlist_inet6(char **haddrlist, size_t addrcount);
static int dstcmp(const void *, const void *);
static int nss_strioctl(int af, int cmd, void *ptr, int ilen);
static struct in_addr _inet_makeaddr(in_addr_t, in_addr_t);
static boolean_t _read_nsw_file(void);

/*
 * Begin: PART I
 * Top Level Interfaces that gethost/serv/netdir funnel through.
 */

static int
inetdir_free(int ret, struct in_addr *inaddrs, char **baddrlist)
{
        if (inaddrs)
                free(inaddrs);
        if (baddrlist)
                free(baddrlist);
        _nderror = ret;
        return (ret);
}

/*
 * gethost/servbyname always call this function; if they call
 * with nametoaddr libs in nconf, we call netdir_getbyname
 * implementation: __classic_netdir_getbyname, otherwise nsswitch.
 *
 * netdir_getbyname calls this only if nametoaddr libs are NOT
 * specified for inet transports; i.e. it's supposed to follow
 * the name service switch.
 */
int
_get_hostserv_inetnetdir_byname(struct netconfig *nconf,
    struct nss_netdirbyname_in *args, union nss_netdirbyname_out *res)
{
        int     server_port;
        int *servp = &server_port;
        char    **haddrlist;
        uint32_t dotnameaddr;
        char    *dotnamelist[2];
        struct in_addr  *inaddrs = NULL;
        struct in6_addr v6nameaddr;
        char    **baddrlist = NULL;

        if (nconf == NULL) {
                _nderror = ND_BADARG;
                return (ND_BADARG);
        }

        /*
         * 1. gethostbyname()/netdir_getbyname() special cases:
         */
        switch (args->op_t) {

                case NSS_HOST:
                /*
                 * Worth the performance gain -- assuming a lot of inet apps
                 * actively use "localhost".
                 */
                if (strcmp(args->arg.nss.host.name, LOCALHOST) == 0) {

                        (void) mutex_lock(&nd_addr_lock);
                        IN_SET_LOOPBACK_ADDR(&sa_con);
                        _nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
                            &nd_conaddrlist, res->nss.host.hent,
                            args->arg.nss.host.buf,
                            args->arg.nss.host.buflen);
                        (void) mutex_unlock(&nd_addr_lock);
                        if (_nderror != ND_OK)
                                *(res->nss.host.herrno_p) =
                                    nd2herrno(_nderror);
                        return (_nderror);
                }
                /*
                 * If the caller passed in a dot separated IP notation to
                 * gethostbyname, return that back as the address.
                 * The nd_addr_lock mutex was added to be truely re-entrant.
                 */
                if (inet_aton(args->arg.nss.host.name,
                    (struct in_addr *)&dotnameaddr)) {
                        (void) mutex_lock(&nd_addr_lock);
                        (void) memset(&sa_con, 0, sizeof (sa_con));
                        sa_con.sin_family = AF_INET;
                        sa_con.sin_addr.s_addr = dotnameaddr;
                        _nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
                            &nd_conaddrlist, res->nss.host.hent,
                            args->arg.nss.host.buf,
                            args->arg.nss.host.buflen);
                        (void) mutex_unlock(&nd_addr_lock);
                        if (_nderror != ND_OK)
                                *(res->nss.host.herrno_p) =
                                    nd2herrno(_nderror);
                        return (_nderror);
                }
                break;

                case NSS_HOST6:
                /*
                 * Handle case of literal address string.
                 */
                if (strchr(args->arg.nss.host6.name, ':') != NULL &&
                    (inet_pton(AF_INET6, args->arg.nss.host6.name,
                    &v6nameaddr) != 0)) {
                        int     ret;

                        (void) mutex_lock(&nd6_addr_lock);
                        (void) memset(&sa6_con, 0, sizeof (sa6_con));
                        sa6_con.sin6_family = AF_INET6;
                        (void) memcpy(&(sa6_con.sin6_addr.s6_addr),
                            &v6nameaddr, sizeof (struct in6_addr));
                        ret = ndaddr2hent(AF_INET6,
                            args->arg.nss.host6.name,
                            &nd6_conaddrlist, res->nss.host.hent,
                            args->arg.nss.host6.buf,
                            args->arg.nss.host6.buflen);
                        (void) mutex_unlock(&nd6_addr_lock);
                        if (ret != ND_OK)
                                *(res->nss.host.herrno_p) = nd2herrno(ret);
                        else
                                res->nss.host.hent->h_aliases = NULL;
                        return (ret);
                }
                break;

                case NETDIR_BY:
                        if (args->arg.nd_hs == 0) {
                                _nderror = ND_BADARG;
                                return (ND_BADARG);
                        }
                        /*
                         * If servname is NULL, return 0 as the port number
                         * If servname is rpcbind, return 111 as the port number
                         * If servname is a number, return it back as the port
                         * number.
                         */
                        if (args->arg.nd_hs->h_serv == 0) {
                                *servp = htons(0);
                        } else if (strcmp(args->arg.nd_hs->h_serv,
                            "rpcbind") == 0) {
                                *servp = htons(111);
                        } else if (strspn(args->arg.nd_hs->h_serv,
                            "0123456789") ==
                            strlen(args->arg.nd_hs->h_serv)) {
                                *servp = htons(atoi(args->arg.nd_hs->h_serv));
                        } else {
                                /* i.e. need to call a name service on this */
                                servp = NULL;
                        }

                        /*
                         * If the hostname is HOST_SELF_BIND, we return 0.0.0.0
                         * so the  binding can be contacted through all
                         * interfaces. If the hostname is HOST_SELF_CONNECT,
                         * we return 127.0.0.1 so the address can be connected
                         * to locally. If the hostname is HOST_ANY, we return
                         * no addresses because IP doesn't know how to specify
                         * a service without a host. And finally if we specify
                         * HOST_BROADCAST then we ask a tli fd to tell us what
                         * the broadcast addresses are for any udp
                         * interfaces on this machine.
                         */
                        if (args->arg.nd_hs->h_host == 0) {
                                _nderror = ND_NOHOST;
                                return (ND_NOHOST);
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            HOST_SELF_BIND) == 0)) {
                                haddrlist = localaddr;
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            HOST_SELF_CONNECT) == 0)) {
                                haddrlist = connectaddr;
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            LOCALHOST) == 0)) {
                                haddrlist = connectaddr;
                        } else if ((int)(dotnameaddr =
                            inet_addr(args->arg.nd_hs->h_host)) != -1) {
                                /*
                                 * If the caller passed in a dot separated IP
                                 * notation to netdir_getbyname, convert that
                                 * back into address.
                                 */

                                dotnamelist[0] = (char *)&dotnameaddr;
                                dotnamelist[1] = NULL;
                                haddrlist = dotnamelist;
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            HOST_BROADCAST) == 0)) {
                                /*
                                 * Now that inaddrs and baddrlist are
                                 * dynamically allocated, care must be
                                 * taken in freeing up the
                                 * memory at each 'return()' point.
                                 *
                                 * Early return protection (using
                                 * inetdir_free()) is needed only in NETDIR_BY
                                 * cases because dynamic allocation is used
                                 * when args->op_t == NETDIR_BY.
                                 *
                                 * Early return protection is not needed in
                                 * haddrlist==0 conditionals because dynamic
                                 * allocation guarantees haddrlist!=0.
                                 *
                                 * Early return protection is not needed in most
                                 * servp!=0 conditionals because this is handled
                                 * (and returned) first.
                                 */
                                int i, bnets;

                                bnets = getbroadcastnets(nconf, &inaddrs);
                                if (bnets == 0) {
                                        _nderror = ND_NOHOST;
                                        return (ND_NOHOST);
                                }
                                baddrlist = malloc((bnets+1)*sizeof (char *));
                                if (baddrlist == NULL)
                                        return (inetdir_free(ND_NOMEM, inaddrs,
                                            baddrlist));
                                for (i = 0; i < bnets; i++)
                                        baddrlist[i] = (char *)&inaddrs[i];
                                baddrlist[i] = NULL;
                                haddrlist = baddrlist;
                        } else {
                                /* i.e. need to call a name service on this */
                                haddrlist = 0;
                        }

                        if (haddrlist && servp) {
                                int ret;
                                /*
                                 * Convert h_addr_list into nd_addrlist.
                                 * malloc's will be done, freed using
                                 * netdir_free.
                                 */
                                ret = hent2ndaddr(AF_INET, haddrlist, servp,
                                    res->nd_alist);
                                return (inetdir_free(ret, inaddrs, baddrlist));
                        }
                        break;


                case NETDIR_BY6:
                        if (args->arg.nd_hs == 0) {
                                _nderror = ND_BADARG;
                                return (ND_BADARG);
                        }
                        /*
                         * If servname is NULL, return 0 as the port number.
                         * If servname is rpcbind, return 111 as the port number
                         * If servname is a number, return it back as the port
                         * number.
                         */
                        if (args->arg.nd_hs->h_serv == 0) {
                                *servp = htons(0);
                        } else if (strcmp(args->arg.nd_hs->h_serv,
                            "rpcbind") == 0) {
                                *servp = htons(111);
                        } else if (strspn(args->arg.nd_hs->h_serv, "0123456789")
                            == strlen(args->arg.nd_hs->h_serv)) {
                                *servp = htons(atoi(args->arg.nd_hs->h_serv));
                        } else {
                                /* i.e. need to call a name service on this */
                                servp = NULL;
                        }

                        /*
                         * If the hostname is HOST_SELF_BIND, we return ipv6
                         * localaddress so the binding can be contacted through
                         * all interfaces.
                         * If the hostname is HOST_SELF_CONNECT, we return
                         * ipv6 loopback address so the address can be connected
                         * to locally.
                         * If the hostname is HOST_ANY, we return no addresses
                         * because IP doesn't know how to specify a service
                         * without a host.
                         * And finally if we specify HOST_BROADCAST then we
                         * disallow since IPV6 does not have any
                         * broadcast concept.
                         */
                        if (args->arg.nd_hs->h_host == 0) {
                                return (ND_NOHOST);
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            HOST_SELF_BIND) == 0)) {
                                haddrlist = localaddr6;
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            HOST_SELF_CONNECT) == 0)) {
                                haddrlist = connectaddr6;
                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            LOCALHOST) == 0)) {
                                haddrlist = connectaddr6;
                        } else if (strchr(args->arg.nd_hs->h_host, ':')
                            != NULL) {

                        /*
                         * If the caller passed in a dot separated IP notation
                         * to netdir_getbyname, convert that back into address.
                         */

                                if ((inet_pton(AF_INET6,
                                    args->arg.nd_hs->h_host,
                                    &v6nameaddr)) != 0) {
                                        dotnamelist[0] = (char *)&v6nameaddr;
                                        dotnamelist[1] = NULL;
                                        haddrlist = dotnamelist;
                                }
                                else
                                        /* not sure what to return */
                                        return (ND_NOHOST);

                        } else if ((strcmp(args->arg.nd_hs->h_host,
                            HOST_BROADCAST) == 0)) {
                                /*
                                 * Don't support broadcast in
                                 * IPV6
                                 */
                                return (ND_NOHOST);
                        } else {
                                /* i.e. need to call a name service on this */
                                haddrlist = 0;
                        }

                        if (haddrlist && servp) {
                                int ret;
                                /*
                                 * Convert h_addr_list into nd_addrlist.
                                 * malloc's will be done, freed
                                 * using netdir_free.
                                 */
                                ret = hent2ndaddr(AF_INET6, haddrlist,
                                    servp, res->nd_alist);
                                return (inetdir_free(ret, inaddrs, baddrlist));
                        }
                        break;


        }

        /*
         * 2. Most common scenario. This is the way we ship /etc/netconfig.
         *    Emphasis on improving performance in the "if" part.
         */
        if (nconf->nc_nlookups == 0) {
                struct hostent  *he = NULL, *tmphe;
                struct servent  *se;
                int     ret;
                nss_XbyY_buf_t  *ndbuf4switch = 0;

        switch (args->op_t) {

                case NSS_HOST:

                he = DOOR_GETHOSTBYNAME_R(args->arg.nss.host.name,
                    res->nss.host.hent, args->arg.nss.host.buf,
                    args->arg.nss.host.buflen,
                    res->nss.host.herrno_p);
                if (he == NULL)
                        return (_nderror = ND_NOHOST);
                return (_nderror = ND_OK);

                case NSS_HOST6:

                he = DOOR_GETIPNODEBYNAME_R(args->arg.nss.host6.name,
                    res->nss.host.hent, args->arg.nss.host.buf,
                    args->arg.nss.host6.buflen,
                    args->arg.nss.host6.af_family,
                    args->arg.nss.host6.flags,
                    res->nss.host.herrno_p);

                if (he == NULL)
                        return (_nderror = ND_NOHOST);
                return (_nderror = ND_OK);

                case NSS_SERV:

                se = _switch_getservbyname_r(args->arg.nss.serv.name,
                    args->arg.nss.serv.proto,
                    res->nss.serv, args->arg.nss.serv.buf,
                    args->arg.nss.serv.buflen);

                _nderror = ND_OK;
                if (se == 0)
                        _nderror = ND_NOSERV;
                return (_nderror);

                case NETDIR_BY:

                if (servp == 0) {
                        char    *proto = (strcmp(nconf->nc_proto,
                            NC_TCP) == 0) ? NC_TCP : NC_UDP;

                        /*
                         * We go through all this for just one port number,
                         * which is most often constant. How about linking in
                         * an indexed database of well-known ports in the name
                         * of performance ?
                         */
                        ndbuf4switch = _nss_XbyY_buf_alloc(
                            sizeof (struct servent), NSS_BUFLEN_SERVICES);
                        if (ndbuf4switch == 0)
                                return (inetdir_free(ND_NOMEM, inaddrs,
                                    baddrlist));
                        se = _switch_getservbyname_r(args->arg.nd_hs->h_serv,
                            proto, ndbuf4switch->result,
                            ndbuf4switch->buffer, ndbuf4switch->buflen);
                        if (!se) {
                                NSS_XbyY_FREE(&ndbuf4switch);
                                return (inetdir_free(ND_NOSERV, inaddrs,
                                    baddrlist));
                        }
                        server_port = se->s_port;
                        NSS_XbyY_FREE(&ndbuf4switch);
                }

                if (haddrlist == 0) {
                        int     h_errnop = 0;

                        ndbuf4switch = _nss_XbyY_buf_alloc(
                            sizeof (struct hostent),
                            NSS_BUFLEN_HOSTS);
                        if (ndbuf4switch == 0) {
                                _nderror = ND_NOMEM;
                                return (ND_NOMEM);
                        }
                        /*
                         * Search the ipnodes (v6) path first,
                         * search will return the v4 addresses
                         * as v4mapped addresses.
                         */
                        if ((tmphe = DOOR_GETIPNODEBYNAME_R(
                            args->arg.nd_hs->h_host,
                            ndbuf4switch->result, ndbuf4switch->buffer,
                            ndbuf4switch->buflen, args->arg.nss.host6.af_family,
                            args->arg.nss.host6.flags, &h_errnop)) != NULL)
                                he = __mappedtov4(tmphe, &h_errnop);

                        if (he == NULL) {
                                /* Failover case, try hosts db for v4 address */
                                he = DOOR_GETHOSTBYNAME_R(
                                    args->arg.nd_hs->h_host,
                                    ndbuf4switch->result, ndbuf4switch->buffer,
                                    ndbuf4switch->buflen, &h_errnop);
                                if (he == NULL) {
                                        NSS_XbyY_FREE(&ndbuf4switch);
                                        _nderror = __herrno2netdir(h_errnop);
                                        return (_nderror);
                                }
                                /*
                                 * Convert h_addr_list into nd_addrlist.
                                 * malloc's will be done, freed using
                                 * netdir_free.
                                 */
                                ret = hent2ndaddr(AF_INET, he->h_addr_list,
                                    &server_port, res->nd_alist);
                        } else {
                                /*
                                 * Convert h_addr_list into nd_addrlist.
                                 * malloc's will be done, freed using
                                 * netdir_free.
                                 */
                                ret = hent2ndaddr(AF_INET, he->h_addr_list,
                                    &server_port, res->nd_alist);
                                freehostent(he);
                        }

                        _nderror = ret;
                        NSS_XbyY_FREE(&ndbuf4switch);
                        return (ret);
                } else {
                        int ret;
                        /*
                         * Convert h_addr_list into nd_addrlist.
                         * malloc's will be done, freed using netdir_free.
                         */
                        ret = hent2ndaddr(AF_INET, haddrlist,
                            &server_port, res->nd_alist);
                        return (inetdir_free(ret, inaddrs, baddrlist));
                }


                case NETDIR_BY6:

                        if (servp == 0) {
                                char    *proto = (strcmp(nconf->nc_proto,
                                    NC_TCP) == 0) ? NC_TCP : NC_UDP;

                                /*
                                 * We go through all this for just
                                 * one port number,
                                 * which is most often constant.
                                 * How about linking in
                                 * an indexed database of well-known
                                 * ports in the name
                                 * of performance ?
                                 */
                                ndbuf4switch = _nss_XbyY_buf_alloc(
                                    sizeof (struct servent),
                                    NSS_BUFLEN_SERVICES);
                                if (ndbuf4switch == 0)
                                        return (inetdir_free(ND_NOMEM, inaddrs,
                                            baddrlist));
                                se = _switch_getservbyname_r(
                                    args->arg.nd_hs->h_serv,
                                    proto, ndbuf4switch->result,
                                    ndbuf4switch->buffer, ndbuf4switch->buflen);
                                if (!se) {
                                        NSS_XbyY_FREE(&ndbuf4switch);
                                        return (inetdir_free(ND_NOSERV, inaddrs,
                                            baddrlist));
                                }
                                server_port = se->s_port;
                                NSS_XbyY_FREE(&ndbuf4switch);
                        }

                        if (haddrlist == 0) {
                                int     h_errnop = 0;

                                ndbuf4switch = _nss_XbyY_buf_alloc(
                                    sizeof (struct hostent),
                                    NSS_BUFLEN_HOSTS);
                                if (ndbuf4switch == 0) {
                                        _nderror = ND_NOMEM;
                                        return (ND_NOMEM);
                                }
                                he = DOOR_GETIPNODEBYNAME_R(
                                    args->arg.nd_hs->h_host,
                                    ndbuf4switch->result, ndbuf4switch->buffer,
                                    ndbuf4switch->buflen,
                                    args->arg.nss.host6.af_family,
                                    args->arg.nss.host6.flags, &h_errnop);
                                if (he == NULL) {
                                        NSS_XbyY_FREE(&ndbuf4switch);
                                        _nderror = __herrno2netdir(h_errnop);
                                        return (_nderror);
                                }
                                /*
                                 * Convert h_addr_list into nd_addrlist.
                                 * malloc's will be done,
                                 * freed using netdir_free.
                                 */
                                ret = hent2ndaddr(AF_INET6,
                                    ((struct hostent *)
                                    (ndbuf4switch->result))->h_addr_list,
                                    &server_port, res->nd_alist);
                                _nderror = ret;
                                NSS_XbyY_FREE(&ndbuf4switch);
                                return (ret);
                        } else {
                                int ret;
                                /*
                                 * Convert h_addr_list into nd_addrlist.
                                 * malloc's will be done,
                                 * freed using netdir_free.
                                 */
                                ret = hent2ndaddr(AF_INET6, haddrlist,
                                    &server_port, res->nd_alist);
                                return (inetdir_free(ret, inaddrs, baddrlist));
                        }

                default:
                        _nderror = ND_BADARG;
                        return (ND_BADARG); /* should never happen */
        }

        } else {
                /* haddrlist is no longer used, so clean up */
                if (inaddrs)
                        free(inaddrs);
                if (baddrlist)
                        free(baddrlist);
        }

        /*
         * 3. We come this far only if nametoaddr libs are specified for
         *    inet transports and we are called by gethost/servbyname only.
         */
        switch (args->op_t) {
                struct  nd_hostserv service;
                struct  nd_addrlist *addrs;
                int ret;

                case NSS_HOST:

                service.h_host = (char *)args->arg.nss.host.name;
                service.h_serv = NULL;
                if ((_nderror = __classic_netdir_getbyname(nconf,
                    &service, &addrs)) != ND_OK) {
                        *(res->nss.host.herrno_p) = nd2herrno(_nderror);
                        return (_nderror);
                }
                /*
                 * convert addresses back into sockaddr for gethostbyname.
                 */
                ret = ndaddr2hent(AF_INET, service.h_host, addrs,
                    res->nss.host.hent, args->arg.nss.host.buf,
                    args->arg.nss.host.buflen);
                if (ret != ND_OK)
                        *(res->nss.host.herrno_p) = nd2herrno(ret);
                netdir_free((char *)addrs, ND_ADDRLIST);
                _nderror = ret;
                return (ret);

                case NSS_SERV:

                if (args->arg.nss.serv.proto == NULL) {
                        /*
                         * A similar HACK showed up in Solaris 2.3.
                         * The caller wild-carded proto -- i.e. will
                         * accept a match using tcp or udp for the port
                         * number. Since we have no hope of getting
                         * directly to a name service switch backend
                         * from here that understands this semantics,
                         * we try calling the netdir interfaces first
                         * with "tcp" and then "udp".
                         */
                        args->arg.nss.serv.proto = "tcp";
                        _nderror = _get_hostserv_inetnetdir_byname(nconf, args,
                            res);
                        if (_nderror != ND_OK) {
                                args->arg.nss.serv.proto = "udp";
                                _nderror =
                                    _get_hostserv_inetnetdir_byname(nconf,
                                    args, res);
                        }
                        return (_nderror);
                }

                /*
                 * Third-parties should optimize their nametoaddr
                 * libraries for the HOST_SELF case.
                 */
                service.h_host = HOST_SELF;
                service.h_serv = (char *)args->arg.nss.serv.name;
                if ((_nderror = __classic_netdir_getbyname(nconf,
                    &service, &addrs)) != ND_OK) {
                        return (_nderror);
                }
                /*
                 * convert addresses back into servent for getservbyname.
                 */
                _nderror = ndaddr2srent(service.h_serv,
                    args->arg.nss.serv.proto,
                    /* LINTED pointer cast */
                    ((struct sockaddr_in *)addrs->n_addrs->buf)->sin_port,
                    res->nss.serv,
                    args->arg.nss.serv.buf, args->arg.nss.serv.buflen);
                netdir_free((char *)addrs, ND_ADDRLIST);
                return (_nderror);

                default:
                _nderror = ND_BADARG;
                return (ND_BADARG); /* should never happen */
        }
}

/*
 * gethostbyaddr/servbyport always call this function; if they call
 * with nametoaddr libs in nconf, we call netdir_getbyaddr
 * implementation __classic_netdir_getbyaddr, otherwise nsswitch.
 *
 * netdir_getbyaddr calls this only if nametoaddr libs are NOT
 * specified for inet transports; i.e. it's supposed to follow
 * the name service switch.
 */
int
_get_hostserv_inetnetdir_byaddr(struct netconfig *nconf,
    struct nss_netdirbyaddr_in *args, union nss_netdirbyaddr_out *res)
{
        if (nconf == 0) {
                _nderror = ND_BADARG;
                return (_nderror);
        }

        /*
         * 1. gethostbyaddr()/netdir_getbyaddr() special cases:
         */
        switch (args->op_t) {

                case NSS_HOST:
                /*
                 * Worth the performance gain: assuming a lot of inet apps
                 * actively use "127.0.0.1".
                 */
                /* LINTED pointer cast */
                if (*(uint32_t *)(args->arg.nss.host.addr) ==
                    htonl(INADDR_LOOPBACK)) {
                        (void) mutex_lock(&nd_addr_lock);
                        IN_SET_LOOPBACK_ADDR(&sa_con);
                        _nderror = ndaddr2hent(AF_INET, LOCALHOST,
                            &nd_conaddrlist, res->nss.host.hent,
                            args->arg.nss.host.buf,
                            args->arg.nss.host.buflen);
                        (void) mutex_unlock(&nd_addr_lock);
                        if (_nderror != ND_OK)
                                *(res->nss.host.herrno_p) =
                                    nd2herrno(_nderror);
                        return (_nderror);
                }
                break;

                case NETDIR_BY:
                case NETDIR_BY_NOSRV:
                {
                        struct sockaddr_in *sin;

                        if (args->arg.nd_nbuf == NULL) {
                                _nderror = ND_BADARG;
                                return (_nderror);
                        }

                        /*
                         * Validate the address which was passed
                         * as the request.
                         */
                        /* LINTED pointer cast */
                        sin = (struct sockaddr_in *)args->arg.nd_nbuf->buf;

                        if ((args->arg.nd_nbuf->len !=
                            sizeof (struct sockaddr_in)) ||
                            (sin->sin_family != AF_INET)) {
                                _nderror = ND_BADARG;
                                return (_nderror);
                        }
                }
                break;

                case NETDIR_BY6:
                case NETDIR_BY_NOSRV6:
                {
                        struct sockaddr_in6 *sin6;

                        if (args->arg.nd_nbuf == NULL) {
                                _nderror = ND_BADARG;
                                return (_nderror);
                        }

                        /*
                         * Validate the address which was passed
                         * as the request.
                         */
                        /* LINTED pointer cast */
                        sin6 = (struct sockaddr_in6 *)args->arg.nd_nbuf->buf;

                        if ((args->arg.nd_nbuf->len !=
                            sizeof (struct sockaddr_in6)) ||
                            (sin6->sin6_family != AF_INET6)) {
                                _nderror = ND_BADARG;
                                return (_nderror);
                        }
                }
                break;

        }

        /*
         * 2. Most common scenario. This is the way we ship /etc/netconfig.
         *    Emphasis on improving performance in the "if" part.
         */
        if (nconf->nc_nlookups == 0) {
                struct hostent  *he = NULL, *tmphe;
                struct servent  *se = NULL;
                nss_XbyY_buf_t  *ndbuf4host = 0;
                nss_XbyY_buf_t  *ndbuf4serv = 0;
                char    *proto =
                    (strcmp(nconf->nc_proto, NC_TCP) == 0) ? NC_TCP : NC_UDP;
                struct  sockaddr_in *sa;
                struct sockaddr_in6 *sin6;
                struct in_addr *addr4 = 0;
                struct in6_addr v4mapbuf;
                int     h_errnop;

        switch (args->op_t) {

                case NSS_HOST:

                he = DOOR_GETHOSTBYADDR_R(args->arg.nss.host.addr,
                    args->arg.nss.host.len, args->arg.nss.host.type,
                    res->nss.host.hent, args->arg.nss.host.buf,
                    args->arg.nss.host.buflen,
                    res->nss.host.herrno_p);
                if (he == 0)
                        _nderror = ND_NOHOST;
                else
                        _nderror = ND_OK;
                return (_nderror);


                case NSS_HOST6:
                he = DOOR_GETIPNODEBYADDR_R(args->arg.nss.host.addr,
                    args->arg.nss.host.len, args->arg.nss.host.type,
                    res->nss.host.hent, args->arg.nss.host.buf,
                    args->arg.nss.host.buflen,
                    res->nss.host.herrno_p);

                if (he == 0)
                        return (ND_NOHOST);
                return (ND_OK);


                case NSS_SERV:

                se = _switch_getservbyport_r(args->arg.nss.serv.port,
                    args->arg.nss.serv.proto,
                    res->nss.serv, args->arg.nss.serv.buf,
                    args->arg.nss.serv.buflen);

                if (se == 0)
                        _nderror = ND_NOSERV;
                else
                        _nderror = ND_OK;
                return (_nderror);

                case NETDIR_BY:
                case NETDIR_BY_NOSRV:

                ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
                    NSS_BUFLEN_SERVICES);
                if (ndbuf4serv == 0) {
                        _nderror = ND_NOMEM;
                        return (_nderror);
                }
                /* LINTED pointer cast */
                sa = (struct sockaddr_in *)(args->arg.nd_nbuf->buf);
                addr4 = (struct in_addr *)&(sa->sin_addr);

                /*
                 * if NETDIR_BY_NOSRV or port == 0 skip the service
                 * lookup.
                 */
                if (args->op_t != NETDIR_BY_NOSRV && sa->sin_port != 0) {
                        se = _switch_getservbyport_r(sa->sin_port, proto,
                            ndbuf4serv->result, ndbuf4serv->buffer,
                            ndbuf4serv->buflen);
                        if (!se) {
                                NSS_XbyY_FREE(&ndbuf4serv);
                                /*
                                 * We can live with this - i.e. the address
                                 * does not
                                 * belong to a well known service. The caller
                                 * traditionally accepts a stringified port
                                 * number
                                 * as the service name. The state of se is used
                                 * ahead to indicate the same.
                                 * However, we do not tolerate this nonsense
                                 * when we cannot get a host name. See below.
                                 */
                        }
                }

                ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
                    NSS_BUFLEN_HOSTS);
                if (ndbuf4host == 0) {
                        if (ndbuf4serv)
                                NSS_XbyY_FREE(&ndbuf4serv);
                        _nderror = ND_NOMEM;
                        return (_nderror);
                }

                /*
                 * Since we're going to search the ipnodes (v6) path first,
                 * we need to treat the address as a v4mapped address.
                 */

                IN6_INADDR_TO_V4MAPPED(addr4, &v4mapbuf);
                if ((tmphe = DOOR_GETIPNODEBYADDR_R((char *)&v4mapbuf,
                    16, AF_INET6, ndbuf4host->result,
                    ndbuf4host->buffer,
                    ndbuf4host->buflen, &h_errnop)) != NULL)
                        he = __mappedtov4(tmphe, &h_errnop);

                if (!he) {
                        /* Failover case, try hosts db for v4 address */
                        he = DOOR_GETHOSTBYADDR_R((char *)
                            &(sa->sin_addr.s_addr), 4,
                            sa->sin_family, ndbuf4host->result,
                            ndbuf4host->buffer, ndbuf4host->buflen,
                            &h_errnop);
                        if (!he) {
                                NSS_XbyY_FREE(&ndbuf4host);
                                if (ndbuf4serv)
                                        NSS_XbyY_FREE(&ndbuf4serv);
                                _nderror = __herrno2netdir(h_errnop);
                                return (_nderror);
                        }
                        /*
                         * Convert host names and service names into hostserv
                         * pairs. malloc's will be done, freed using
                         * netdir_free.
                         */
                        h_errnop = hsents2ndhostservs(he, se,
                            sa->sin_port, res->nd_hslist);
                } else {
                        /*
                         * Convert host names and service names into hostserv
                         * pairs. malloc's will be done, freed using
                         * netdir_free.
                         */
                        h_errnop = hsents2ndhostservs(he, se,
                            sa->sin_port, res->nd_hslist);
                        freehostent(he);
                }

                NSS_XbyY_FREE(&ndbuf4host);
                if (ndbuf4serv)
                        NSS_XbyY_FREE(&ndbuf4serv);
                _nderror = __herrno2netdir(h_errnop);
                return (_nderror);

                case NETDIR_BY6:
                case NETDIR_BY_NOSRV6:

                ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
                    NSS_BUFLEN_SERVICES);
                if (ndbuf4serv == 0) {
                        _nderror = ND_NOMEM;
                        return (ND_NOMEM);
                }
                /* LINTED pointer cast */
                sin6 = (struct sockaddr_in6 *)(args->arg.nd_nbuf->buf);

                /*
                 * if NETDIR_BY_NOSRV6 or port == 0 skip the service
                 * lookup.
                 */
                if (args->op_t != NETDIR_BY_NOSRV6 && sin6->sin6_port == 0) {
                        se = _switch_getservbyport_r(sin6->sin6_port, proto,
                            ndbuf4serv->result, ndbuf4serv->buffer,
                            ndbuf4serv->buflen);
                        if (!se) {
                                NSS_XbyY_FREE(&ndbuf4serv);
                                /*
                                 * We can live with this - i.e. the address does
                                 * not * belong to a well known service. The
                                 * caller traditionally accepts a stringified
                                 * port number
                                 * as the service name. The state of se is used
                                 * ahead to indicate the same.
                                 * However, we do not tolerate this nonsense
                                 * when we cannot get a host name. See below.
                                 */
                        }
                }

                ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
                    NSS_BUFLEN_HOSTS);
                if (ndbuf4host == 0) {
                        if (ndbuf4serv)
                                NSS_XbyY_FREE(&ndbuf4serv);
                        _nderror = ND_NOMEM;
                        return (_nderror);
                }
                he = DOOR_GETIPNODEBYADDR_R((char *)&(sin6->sin6_addr),
                    16, sin6->sin6_family, ndbuf4host->result,
                    ndbuf4host->buffer,
                    ndbuf4host->buflen, &h_errnop);
                if (!he) {
                        NSS_XbyY_FREE(&ndbuf4host);
                        if (ndbuf4serv)
                                NSS_XbyY_FREE(&ndbuf4serv);
                        _nderror = __herrno2netdir(h_errnop);
                        return (_nderror);
                }
                /*
                 * Convert host names and service names into hostserv
                 * pairs. malloc's will be done, freed using netdir_free.
                 */
                h_errnop = hsents2ndhostservs(he, se,
                    sin6->sin6_port, res->nd_hslist);

                NSS_XbyY_FREE(&ndbuf4host);
                if (ndbuf4serv)
                        NSS_XbyY_FREE(&ndbuf4serv);
                _nderror = __herrno2netdir(h_errnop);
                return (_nderror);

                default:
                _nderror = ND_BADARG;
                return (_nderror); /* should never happen */
        }

        }
        /*
         * 3. We come this far only if nametoaddr libs are specified for
         *    inet transports and we are called by gethost/servbyname only.
         */
        switch (args->op_t) {
                struct  netbuf nbuf;
                struct  nd_hostservlist *addrs;
                struct  sockaddr_in sa;

                case NSS_HOST:

                /* LINTED pointer cast */
                sa.sin_addr.s_addr = *(uint32_t *)args->arg.nss.host.addr;
                sa.sin_family = AF_INET;
                /* Hopefully, third-parties get this optimization */
                sa.sin_port = 0;
                nbuf.buf = (char *)&sa;
                nbuf.len = nbuf.maxlen = sizeof (sa);
                if ((_nderror = __classic_netdir_getbyaddr(nconf,
                    &addrs, &nbuf)) != 0) {
                        *(res->nss.host.herrno_p) = nd2herrno(_nderror);
                        return (_nderror);
                }
                /*
                 * convert the host-serv pairs into h_aliases and hent.
                 */
                _nderror = ndhostserv2hent(&nbuf, addrs, res->nss.host.hent,
                    args->arg.nss.host.buf, args->arg.nss.host.buflen);
                if (_nderror != ND_OK)
                        *(res->nss.host.herrno_p) = nd2herrno(_nderror);
                netdir_free((char *)addrs, ND_HOSTSERVLIST);
                return (_nderror);

                case NSS_SERV:

                if (args->arg.nss.serv.proto == NULL) {
                        /*
                         * A similar HACK showed up in Solaris 2.3.
                         * The caller wild-carded proto -- i.e. will
                         * accept a match on tcp or udp for the port
                         * number. Since we have no hope of getting
                         * directly to a name service switch backend
                         * from here that understands this semantics,
                         * we try calling the netdir interfaces first
                         * with "tcp" and then "udp".
                         */
                        args->arg.nss.serv.proto = "tcp";
                        _nderror = _get_hostserv_inetnetdir_byaddr(nconf, args,
                            res);
                        if (_nderror != ND_OK) {
                                args->arg.nss.serv.proto = "udp";
                                _nderror =
                                    _get_hostserv_inetnetdir_byaddr(nconf,
                                    args, res);
                        }
                        return (_nderror);
                }

                /*
                 * Third-party nametoaddr_libs should be optimized for
                 * this case. It also gives a special semantics twist to
                 * netdir_getbyaddr. Only for the INADDR_ANY case, it gives
                 * higher priority to service lookups (over host lookups).
                 * If service lookup fails, the backend returns ND_NOSERV to
                 * facilitate lookup in the "next" naming service.
                 * BugId: 1075403.
                 */
                sa.sin_addr.s_addr = INADDR_ANY;
                sa.sin_family = AF_INET;
                sa.sin_port = (ushort_t)args->arg.nss.serv.port;
                sa.sin_zero[0] = '\0';
                nbuf.buf = (char *)&sa;
                nbuf.len = nbuf.maxlen = sizeof (sa);
                if ((_nderror = __classic_netdir_getbyaddr(nconf,
                    &addrs, &nbuf)) != ND_OK) {
                        return (_nderror);
                }
                /*
                 * convert the host-serv pairs into s_aliases and servent.
                 */
                _nderror = ndhostserv2srent(args->arg.nss.serv.port,
                    args->arg.nss.serv.proto, addrs, res->nss.serv,
                    args->arg.nss.serv.buf, args->arg.nss.serv.buflen);
                netdir_free((char *)addrs, ND_HOSTSERVLIST);
                return (_nderror);

                default:
                _nderror = ND_BADARG;
                return (_nderror); /* should never happen */
        }
}

/*
 * Part II: Name Service Switch interfacing routines.
 */

static DEFINE_NSS_DB_ROOT(db_root_hosts);
static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
static DEFINE_NSS_DB_ROOT(db_root_services);


/*
 * There is a copy of __nss2herrno() in nsswitch/files/gethostent.c.
 * It is there because /etc/lib/nss_files.so.1 cannot call
 * routines in libnsl.  Care should be taken to keep the two copies
 * in sync (except that case NSS_NISSERVDNS_TRYAGAIN is not needed in
 * nsswitch/files).
 */
int
__nss2herrno(nss_status_t nsstat)
{
        switch (nsstat) {
        case NSS_SUCCESS:
                /* no macro-defined success code for h_errno */
                return (0);
        case NSS_NOTFOUND:
                return (HOST_NOT_FOUND);
        case NSS_TRYAGAIN:
                return (TRY_AGAIN);
        case NSS_UNAVAIL:
                return (NO_RECOVERY);
        case NSS_NISSERVDNS_TRYAGAIN:
                return (TRY_AGAIN);
        }
        /* anything else */
        return (NO_RECOVERY);
}

nss_status_t
_herrno2nss(int h_errno)
{
        switch (h_errno) {
        case 0:
                return (NSS_SUCCESS);
        case TRY_AGAIN:
                return (NSS_TRYAGAIN);
        case NO_RECOVERY:
        case NETDB_INTERNAL:
                return (NSS_UNAVAIL);
        case HOST_NOT_FOUND:
        case NO_DATA:
        default:
                return (NSS_NOTFOUND);
        }
}

static int
__herrno2netdir(int h_errnop)
{
        switch (h_errnop) {
                case 0:
                        return (ND_OK);
                case HOST_NOT_FOUND:
                        return (ND_NOHOST);
                case TRY_AGAIN:
                        return (ND_TRY_AGAIN);
                case NO_RECOVERY:
                case NETDB_INTERNAL:
                        return (ND_NO_RECOVERY);
                case NO_DATA:
                        return (ND_NO_DATA);
                default:
                        return (ND_NOHOST);
        }
}

/*
 * The _switch_getXXbyYY_r() routines should be static.  They used to
 * be exported in SunOS 5.3, and in fact publicised as work-around
 * interfaces for getting CNAME/aliases, and therefore, we preserve
 * their signatures here. Just in case.
 */

struct hostent *
_switch_gethostbyname_r(const char *name, struct hostent *result, char *buffer,
    int buflen, int *h_errnop)
{
        nss_XbyY_args_t arg;
        nss_status_t    res;

        NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
        arg.key.name    = name;
        arg.stayopen    = 0;
        res = nss_search(&db_root_hosts, _nss_initf_hosts,
            NSS_DBOP_HOSTS_BYNAME, &arg);
        arg.status = res;
        if (res != NSS_SUCCESS)
                *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
        if (arg.returnval != NULL)
                order_haddrlist_af(result->h_addrtype, result->h_addr_list);
        return ((struct hostent *)NSS_XbyY_FINI(&arg));
}

struct hostent *
_switch_getipnodebyname_r(const char *name, struct hostent *result,
    char *buffer, int buflen, int af_family, int flags, int *h_errnop)
{
        nss_XbyY_args_t arg;
        nss_status_t    res;

        NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
        arg.key.ipnode.name     = name;
        arg.key.ipnode.af_family = af_family;
        arg.key.ipnode.flags = flags;
        arg.stayopen    = 0;
        res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
            NSS_DBOP_IPNODES_BYNAME, &arg);
        arg.status = res;
        if (res != NSS_SUCCESS)
                *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
        if (arg.returnval != NULL)
                order_haddrlist_af(result->h_addrtype, result->h_addr_list);
        return ((struct hostent *)NSS_XbyY_FINI(&arg));
}

struct hostent *
_switch_gethostbyaddr_r(const char *addr, int len, int type,
    struct hostent *result, char *buffer, int buflen, int *h_errnop)
{
        nss_XbyY_args_t arg;
        nss_status_t    res;

        NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
        arg.key.hostaddr.addr   = addr;
        arg.key.hostaddr.len    = len;
        arg.key.hostaddr.type   = type;
        arg.stayopen            = 0;
        res = nss_search(&db_root_hosts, _nss_initf_hosts,
            NSS_DBOP_HOSTS_BYADDR, &arg);
        arg.status = res;
        if (res != NSS_SUCCESS)
                *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
        return (struct hostent *)NSS_XbyY_FINI(&arg);
}

struct hostent *
_switch_getipnodebyaddr_r(const char *addr, int len, int type,
    struct hostent *result, char *buffer, int buflen, int *h_errnop)
{
        nss_XbyY_args_t arg;
        nss_status_t    res;

        NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
        arg.key.hostaddr.addr   = addr;
        arg.key.hostaddr.len    = len;
        arg.key.hostaddr.type   = type;
        arg.stayopen            = 0;
        res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
            NSS_DBOP_IPNODES_BYADDR, &arg);
        arg.status = res;
        if (res != NSS_SUCCESS)
                *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
        return (struct hostent *)NSS_XbyY_FINI(&arg);
}

static void
_nss_initf_services(nss_db_params_t *p)
{
        p->name = NSS_DBNAM_SERVICES;
        p->default_config = NSS_DEFCONF_SERVICES;
}

struct servent *
_switch_getservbyname_r(const char *name, const char *proto,
    struct servent *result, char *buffer, int buflen)
{
        nss_XbyY_args_t arg;
        nss_status_t    res;

        NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
        arg.key.serv.serv.name  = name;
        arg.key.serv.proto      = proto;
        arg.stayopen            = 0;
        res = nss_search(&db_root_services, _nss_initf_services,
            NSS_DBOP_SERVICES_BYNAME, &arg);
        arg.status = res;
        return ((struct servent *)NSS_XbyY_FINI(&arg));
}

struct servent *
_switch_getservbyport_r(int port, const char *proto, struct servent *result,
    char *buffer, int buflen)
{
        nss_XbyY_args_t arg;
        nss_status_t    res;

        NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
        arg.key.serv.serv.port  = port;
        arg.key.serv.proto      = proto;
        arg.stayopen            = 0;
        res = nss_search(&db_root_services, _nss_initf_services,
            NSS_DBOP_SERVICES_BYPORT, &arg);
        arg.status = res;
        return ((struct servent *)NSS_XbyY_FINI(&arg));
}


/*
 * Return values: 0 = success, 1 = parse error, 2 = erange ...
 * The structure pointer passed in is a structure in the caller's space
 * wherein the field pointers would be set to areas in the buffer if
 * need be. instring and buffer should be separate areas.
 *
 * Defined here because we need it and we (libnsl) cannot have a dependency
 * on libsocket (however, libsocket always depends on libnsl).
 */
int
str2servent(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
{
        struct servent  *serv   = (struct servent *)ent;
        const char      *p, *fieldstart, *limit, *namestart;
        ssize_t         fieldlen, namelen = 0;
        char            numbuf[12];
        char            *numend;

        if ((instr >= buffer && (buffer + buflen) > instr) ||
            (buffer >= instr && (instr + lenstr) > buffer)) {
                return (NSS_STR_PARSE_PARSE);
        }

        p = instr;
        limit = p + lenstr;

        while (p < limit && isspace(*p)) {
                p++;
        }
        namestart = p;
        while (p < limit && !isspace(*p)) {
                p++;            /* Skip over the canonical name */
        }
        namelen = p - namestart;

        if (buflen <= namelen) { /* not enough buffer */
                return (NSS_STR_PARSE_ERANGE);
        }
        (void) memcpy(buffer, namestart, namelen);
        buffer[namelen] = '\0';
        serv->s_name = buffer;

        while (p < limit && isspace(*p)) {
                p++;
        }

        fieldstart = p;
        do {
                if (p > limit || isspace(*p)) {
                        /* Syntax error -- no port/proto */
                        return (NSS_STR_PARSE_PARSE);
                }
        } while (*p++ != '/');
        fieldlen = p - fieldstart - 1;
        if (fieldlen == 0 || fieldlen >= sizeof (numbuf)) {
                /* Syntax error -- supposed number is empty or too long */
                return (NSS_STR_PARSE_PARSE);
        }
        (void) memcpy(numbuf, fieldstart, fieldlen);
        numbuf[fieldlen] = '\0';
        serv->s_port = htons((int)strtol(numbuf, &numend, 10));
        if (*numend != '\0') {
                /* Syntax error -- port number isn't a number */
                return (NSS_STR_PARSE_PARSE);
        }

        fieldstart = p;
        while (p < limit && !isspace(*p)) {
                p++;            /* Scan the protocol name */
        }
        fieldlen = p - fieldstart + 1;          /* Include '\0' this time */
        if (fieldlen > buflen - namelen - 1) {
                return (NSS_STR_PARSE_ERANGE);
        }
        serv->s_proto = buffer + namelen + 1;
        (void) memcpy(serv->s_proto, fieldstart, fieldlen - 1);
        serv->s_proto[fieldlen - 1] = '\0';

        while (p < limit && isspace(*p)) {
                p++;
        }
        /*
         * Although nss_files_XY_all calls us with # stripped,
         * we should be able to deal with it here in order to
         * be more useful.
         */
        if (p >= limit || *p == '#') { /* no aliases, no problem */
                char **ptr;

                ptr = (char **)ROUND_UP(buffer + namelen + 1 + fieldlen,
                    sizeof (char *));
                if ((char *)ptr >= buffer + buflen) {
                        /* hope they don't try to peek in */
                        serv->s_aliases = 0;
                        return (NSS_STR_PARSE_ERANGE);
                } else {
                        *ptr = 0;
                        serv->s_aliases = ptr;
                        return (NSS_STR_PARSE_SUCCESS);
                }
        }
        serv->s_aliases = _nss_netdb_aliases(p, (int)(lenstr - (p - instr)),
            buffer + namelen + 1 + fieldlen,
            (int)(buflen - namelen - 1 - fieldlen));
        return (NSS_STR_PARSE_SUCCESS);
}

/*
 * Part III: All `n sundry routines that are useful only in this
 * module. In the interest of keeping this source file shorter,
 * we would create them a new module only if the linker allowed
 * "library-static" functions.
 *
 * Routines to order addresses based on local interfaces and netmasks,
 * to get and check reserved ports, and to get broadcast nets.
 */

union __v4v6addr {
        struct in6_addr in6;
        struct in_addr  in4;
};

struct __ifaddr {
        sa_family_t             af;
        union __v4v6addr        addr;
        union __v4v6addr        mask;
};

struct ifinfo {
        int             count;
        struct __ifaddr *addresses;
};

typedef enum {ADDR_ONLINK = 0, ADDR_OFFLINK} addr_class_t;
#define ADDR_NUMCLASSES 2

typedef enum {IF_ADDR, IF_MASK} __ifaddr_type;
static int      __inet_ifassign(sa_family_t, struct __ifaddr *, __ifaddr_type,
                                void *);
int             __inet_address_is_local_af(void *, sa_family_t, void *);

#define ifaf(index)     (localinfo->addresses[index].af)
#define ifaddr4(index)  (localinfo->addresses[index].addr.in4)
#define ifaddr6(index)  (localinfo->addresses[index].addr.in6)
#define ifmask4(index)  (localinfo->addresses[index].mask.in4)
#define ifmask6(index)  (localinfo->addresses[index].mask.in6)
#define ifinfosize(n)   (sizeof (struct ifinfo) + (n)*sizeof (struct __ifaddr))

#define lifraddrp(lifr) ((lifr.lifr_addr.ss_family == AF_INET6) ? \
        (void *)&((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr : \
        (void *)&((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr)

#define ifassign(lifr, index, type) \
                        __inet_ifassign(lifr.lifr_addr.ss_family, \
                                &localinfo->addresses[index], type, \
                                lifraddrp(lifr))

/*
 * The number of nanoseconds the order_haddrlist_inet() function waits
 * to retreive IP interface information.  The default is five minutes.
 */
#define IFINFOTIMEOUT   ((hrtime_t)300 * NANOSEC)

/*
 * Sort the addresses in haddrlist.  Since the sorting algorithms are
 * address-family specific, the work is done in the address-family
 * specific order_haddrlist_<family> functions.
 *
 * Do not sort addresses if SORT_ADDRS variable is set to NO or FALSE
 * in the configuration file /etc/default/nss. This is useful in case
 * the order of addresses returned by the nameserver needs to be
 * maintained. (DNS round robin feature is one example)
 */
void
order_haddrlist_af(sa_family_t af, char **haddrlist)
{
        size_t                  addrcount;
        char                    **addrptr;
        static boolean_t        checksortcfg = B_TRUE;
        static boolean_t        nosort = B_FALSE;
        static mutex_t          checksortcfg_lock = DEFAULTMUTEX;

        if (haddrlist == NULL)
                return;

        /*
         * Check if SORT_ADDRS is set to NO or FALSE in the configuration
         * file.  We do not have to sort addresses in that case.
         */
        (void) mutex_lock(&checksortcfg_lock);
        if (checksortcfg == B_TRUE) {
                checksortcfg = B_FALSE;
                nosort = _read_nsw_file();
        }
        (void) mutex_unlock(&checksortcfg_lock);

        if (nosort)
                return;

        /* Count the addresses to sort */
        addrcount = 0;
        for (addrptr = haddrlist; *addrptr != NULL; addrptr++)
                addrcount++;

        /*
         * If there's only one address or no addresses to sort, then
         * there's nothing for us to do.
         */
        if (addrcount <= 1)
                return;

        /* Call the address-family specific sorting functions. */
        switch (af) {
        case AF_INET:
                order_haddrlist_inet(haddrlist, addrcount);
                break;
        case AF_INET6:
                order_haddrlist_inet6(haddrlist, addrcount);
                break;
        default:
                break;
        }
}

/*
 * Move any local (on-link) addresses toward the beginning of haddrlist.
 * The order within these two classes is preserved.
 *
 * The interface list is retrieved no more often than every
 * IFINFOTIMEOUT nanoseconds. Access to the interface list is
 * protected by an RW lock.
 *
 * If this function encounters an error, haddrlist is unaltered.
 */
static void
order_haddrlist_inet(char **haddrlist, size_t addrcount)
{
        static struct   ifinfo *localinfo = NULL;
        static hrtime_t then = 0; /* the last time localinfo was updated */
        hrtime_t        now;
        static rwlock_t localinfo_lock = DEFAULTRWLOCK;
        uint8_t         *sortbuf;
        size_t          sortbuf_size;
        struct in_addr  **inaddrlist = (struct in_addr **)haddrlist;
        struct in_addr  **sorted;
        struct in_addr  **classnext[ADDR_NUMCLASSES];
        uint_t          classcount[ADDR_NUMCLASSES];
        addr_class_t    *sortclass;
        int             i;
        int             rc;


        /*
         * The classes in the sortclass array correspond to the class
         * of the address in the haddrlist list of the same index.
         * The classes are:
         *
         * ADDR_ONLINK  on-link address
         * ADDR_OFFLINK off-link address
         */
        sortbuf_size = addrcount *
            (sizeof (struct in_addr *) + sizeof (addr_class_t));
        if ((sortbuf = malloc(sortbuf_size)) == NULL)
                return;
        /* LINTED pointer cast */
        sorted = (struct in_addr **)sortbuf;
        /* LINTED pointer cast */
        sortclass = (addr_class_t *)(sortbuf +
            (addrcount * sizeof (struct in_addr *)));

        /*
         * Get a read lock, and check if the interface information
         * is too old.
         */
        (void) rw_rdlock(&localinfo_lock);
        now = gethrtime();
        if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
                /* Need to update I/F info. Upgrade to write lock. */
                (void) rw_unlock(&localinfo_lock);
                (void) rw_wrlock(&localinfo_lock);
                /*
                 * Another thread might have updated "then" between
                 * the rw_unlock() and rw_wrlock() calls above, so
                 * re-check the timeout.
                 */
                if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
                        if (localinfo != NULL)
                                free(localinfo);
                        if ((localinfo = get_local_info()) == NULL) {
                                (void) rw_unlock(&localinfo_lock);
                                free(sortbuf);
                                return;
                        }
                        then = now;
                }
                /* Downgrade to read lock */
                (void) rw_unlock(&localinfo_lock);
                (void) rw_rdlock(&localinfo_lock);
                /*
                 * Another thread may have updated the I/F info,
                 * so verify that the 'localinfo' pointer still
                 * is non-NULL.
                 */
                if (localinfo == NULL) {
                        (void) rw_unlock(&localinfo_lock);
                        free(sortbuf);
                        return;
                }
        }

        /*
         * Classify the addresses.  We also maintain the classcount
         * array to keep track of the number of addresses in each
         * class.
         */
        (void) memset(classcount, 0, sizeof (classcount));
        for (i = 0; i < addrcount; i++) {
                if (__inet_address_is_local_af(localinfo, AF_INET,
                    inaddrlist[i]))
                        sortclass[i] = ADDR_ONLINK;
                else
                        sortclass[i] = ADDR_OFFLINK;
                classcount[sortclass[i]]++;
        }

        /* Don't need the interface list anymore in this call */
        (void) rw_unlock(&localinfo_lock);

        /*
         * Each element in the classnext array points to the next
         * element for that class in the sorted address list. 'rc' is
         * the running count of elements as we sum the class
         * sub-totals.
         */
        for (rc = 0, i = 0; i < ADDR_NUMCLASSES; i++) {
                classnext[i] = &sorted[rc];
                rc += classcount[i];
        }

        /* Now for the actual rearrangement of the addresses */
        for (i = 0; i < addrcount; i++) {
                *(classnext[sortclass[i]]) = inaddrlist[i];
                classnext[sortclass[i]]++;
        }

        /* Copy the sorted list to inaddrlist */
        (void) memcpy(inaddrlist, sorted,
            addrcount * sizeof (struct in_addr *));
        free(sortbuf);
}

/*
 * This function implements the IPv6 Default Address Selection's
 * destination address ordering mechanism.  The algorithm is described
 * in getaddrinfo(3SOCKET).
 */
static void
order_haddrlist_inet6(char **haddrlist, size_t addrcount)
{
        struct dstinforeq *dinfo, *dinfoptr;
        struct in6_addr **in6addrlist = (struct in6_addr **)haddrlist;
        struct in6_addr **in6addr;

        if ((dinfo = calloc(addrcount, sizeof (struct dstinforeq))) == NULL)
                return;

        /* Initialize the dstinfo array we'll use for SIOCGDSTINFO */
        dinfoptr = dinfo;
        for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
                dinfoptr->dir_daddr = **in6addr;
                dinfoptr++;
        }

        if (nss_strioctl(AF_INET6, SIOCGDSTINFO, dinfo,
            addrcount * sizeof (struct dstinforeq)) < 0) {
                free(dinfo);
                return;
        }

        /* Sort the dinfo array */
        qsort(dinfo, addrcount, sizeof (struct dstinforeq), dstcmp);

        /* Copy the addresses back into in6addrlist */
        dinfoptr = dinfo;
        for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
                **in6addr = dinfoptr->dir_daddr;
                dinfoptr++;
        }

        free(dinfo);
}

/*
 * Determine number of leading bits that are common between two addresses.
 * Only consider bits which fall within the prefix length plen.
 */
static uint_t
ip_addr_commonbits_v6(const in6_addr_t *a1, const in6_addr_t *a2)
{
        uint_t          bits;
        uint_t          i;
        uint32_t        diff;   /* Bits that differ */

        for (i = 0; i < 4; i++) {
                if (a1->_S6_un._S6_u32[i] != a2->_S6_un._S6_u32[i])
                        break;
        }
        bits = i * 32;

        if (bits == IPV6_ABITS)
                return (IPV6_ABITS);

        /*
         * Find number of leading common bits in the word which might
         * have some common bits by searching for the first one from the left
         * in the xor of the two addresses.
         */
        diff = ntohl(a1->_S6_un._S6_u32[i] ^ a2->_S6_un._S6_u32[i]);
        if (diff & 0xffff0000ul)
                diff >>= 16;
        else
                bits += 16;
        if (diff & 0xff00)
                diff >>= 8;
        else
                bits += 8;
        if (diff & 0xf0)
                diff >>= 4;
        else
                bits += 4;
        if (diff & 0xc)
                diff >>= 2;
        else
                bits += 2;
        if (!(diff & 2))
                bits++;

        /*
         * We don't need to shift and check for the last bit.  The
         * check for IPV6_ABITS above would have caught that.
         */

        return (bits);
}


/*
 * The following group of functions named rule_*() are individual
 * sorting rules for the AF_INET6 address sorting algorithm.  The
 * functions compare two addresses (described by two dstinforeq
 * structures), and determines if one is "greater" than the other, or
 * if the two are equal according to that rule.
 */
typedef int (*rulef_t)(const struct dstinforeq *, const struct dstinforeq *);

/*
 * These values of these constants are no accident.  Since qsort()
 * implements the AF_INET6 address sorting, the comparison function
 * must return an integer less than, equal to, or greater than zero to
 * indicate if the first address is considered "less than", "equal
 * to", or "greater than" the second one.  Since we want the best
 * addresses first on the list, "less than" is considered preferrable.
 */
#define RULE_PREFER_DA  -1
#define RULE_PREFER_DB  1
#define RULE_EQUAL      0

/* Prefer the addresses that is reachable. */
static int
rule_reachable(const struct dstinforeq *da, const struct dstinforeq *db)
{
        if (da->dir_dreachable == db->dir_dreachable)
                return (RULE_EQUAL);
        if (da->dir_dreachable)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/* Prefer the address whose scope matches that of its source address. */
static int
rule_matchscope(const struct dstinforeq *da, const struct dstinforeq *db)
{
        boolean_t da_scope_match, db_scope_match;

        da_scope_match = da->dir_dscope == da->dir_sscope;
        db_scope_match = db->dir_dscope == db->dir_sscope;

        if (da_scope_match == db_scope_match)
                return (RULE_EQUAL);
        if (da_scope_match)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/* Avoid the address with the link local source address. */
static int
rule_avoidlinklocal(const struct dstinforeq *da, const struct dstinforeq *db)
{
        if (da->dir_sscope == IP6_SCOPE_LINKLOCAL &&
            da->dir_dscope != IP6_SCOPE_LINKLOCAL &&
            db->dir_sscope != IP6_SCOPE_LINKLOCAL)
                return (RULE_PREFER_DB);
        if (db->dir_sscope == IP6_SCOPE_LINKLOCAL &&
            db->dir_dscope != IP6_SCOPE_LINKLOCAL &&
            da->dir_sscope != IP6_SCOPE_LINKLOCAL)
                return (RULE_PREFER_DA);
        return (RULE_EQUAL);
}

/* Prefer the address whose source address isn't deprecated. */
static int
rule_deprecated(const struct dstinforeq *da, const struct dstinforeq *db)
{
        if (da->dir_sdeprecated == db->dir_sdeprecated)
                return (RULE_EQUAL);
        if (db->dir_sdeprecated)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/* Prefer the address whose label matches that of its source address. */
static int
rule_label(const struct dstinforeq *da, const struct dstinforeq *db)
{
        if (da->dir_labelmatch == db->dir_labelmatch)
                return (RULE_EQUAL);
        if (da->dir_labelmatch)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/* Prefer the address with the higher precedence. */
static int
rule_precedence(const struct dstinforeq *da, const struct dstinforeq *db)
{
        if (da->dir_precedence == db->dir_precedence)
                return (RULE_EQUAL);
        if (da->dir_precedence > db->dir_precedence)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/* Prefer the address whose output interface isn't an IP tunnel */
static int
rule_native(const struct dstinforeq *da, const struct dstinforeq *db)
{
        boolean_t isatun, isbtun;

        /* Get the common case out of the way early */
        if (da->dir_dmactype == db->dir_dmactype)
                return (RULE_EQUAL);

        isatun = da->dir_dmactype == DL_IPV4 || da->dir_dmactype == DL_IPV6;
        isbtun = db->dir_dmactype == DL_IPV4 || db->dir_dmactype == DL_IPV6;

        if (isatun == isbtun)
                return (RULE_EQUAL);
        if (isbtun)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/* Prefer the address with the smaller scope. */
static int
rule_scope(const struct dstinforeq *da, const struct dstinforeq *db)
{
        if (da->dir_dscope == db->dir_dscope)
                return (RULE_EQUAL);
        if (da->dir_dscope < db->dir_dscope)
                return (RULE_PREFER_DA);
        return (RULE_PREFER_DB);
}

/*
 * Prefer the address that has the most leading bits in common with its
 * source address.
 */
static int
rule_prefix(const struct dstinforeq *da, const struct dstinforeq *db)
{
        uint_t da_commonbits, db_commonbits;
        boolean_t da_isipv4, db_isipv4;

        da_isipv4 = IN6_IS_ADDR_V4MAPPED(&da->dir_daddr);
        db_isipv4 = IN6_IS_ADDR_V4MAPPED(&db->dir_daddr);

        /*
         * At this point, the order doesn't matter if the two addresses
         * aren't of the same address family.
         */
        if (da_isipv4 != db_isipv4)
                return (RULE_EQUAL);

        da_commonbits = ip_addr_commonbits_v6(&da->dir_daddr, &da->dir_saddr);
        db_commonbits = ip_addr_commonbits_v6(&db->dir_daddr, &db->dir_saddr);

        if (da_commonbits > db_commonbits)
                return (RULE_PREFER_DA);
        if (da_commonbits < db_commonbits)
                return (RULE_PREFER_DB);
        return (RULE_EQUAL);
}

/*
 * This is the function passed to qsort() that does the AF_INET6
 * address comparisons.  It compares two addresses using a list of
 * rules.  The rules are applied in order until one prefers one
 * address over the other.
 */
static int
dstcmp(const void *da, const void *db)
{
        int index, result;
        rulef_t rules[] = {
            rule_reachable,
            rule_matchscope,
            rule_avoidlinklocal,
            rule_deprecated,
            rule_label,
            rule_precedence,
            rule_native,
            rule_scope,
            rule_prefix,
            NULL
        };

        result = 0;
        for (index = 0; rules[index] != NULL; index++) {
                result = (rules[index])(da, db);
                if (result != RULE_EQUAL)
                        break;
        }

        return (result);
}

/*
 * Given haddrlist and a port number, mallocs and populates a new
 * nd_addrlist.  The new nd_addrlist maintains the order of the addresses
 * in haddrlist, which have already been sorted by order_haddrlist_inet()
 * or order_haddrlist_inet6().  For IPv6 this function filters out
 * IPv4-mapped IPv6 addresses.
 */
int
hent2ndaddr(int af, char **haddrlist, int *servp, struct nd_addrlist **nd_alist)
{
        struct nd_addrlist      *result;
        int                     num;
        struct netbuf           *na;
        struct sockaddr_in      *sinbuf, *sin;
        struct sockaddr_in6     *sin6buf, *sin6;
        struct in_addr          **inaddr, **inaddrlist;
        struct in6_addr         **in6addr, **in6addrlist;

        /* Address count */
        num = 0;
        if (af == AF_INET6) {
                in6addrlist = (struct in6_addr **)haddrlist;

                /*
                 * Exclude IPv4-mapped IPv6 addresses from the count, as
                 * these are not included in the nd_addrlist we return.
                 */
                for (in6addr = in6addrlist; *in6addr != NULL; in6addr++)
                        if (!IN6_IS_ADDR_V4MAPPED(*in6addr))
                                num++;
        } else {
                inaddrlist = (struct in_addr **)haddrlist;

                for (inaddr = inaddrlist; *inaddr != NULL; inaddr++)
                        num++;
        }
        if (num == 0)
                return (ND_NOHOST);

        result = malloc(sizeof (struct nd_addrlist));
        if (result == 0)
                return (ND_NOMEM);

        result->n_cnt = num;
        result->n_addrs = calloc(num, sizeof (struct netbuf));
        if (result->n_addrs == 0) {
                free(result);
                return (ND_NOMEM);
        }

        na = result->n_addrs;
        if (af == AF_INET) {
                sinbuf = calloc(num, sizeof (struct sockaddr_in));
                if (sinbuf == NULL) {
                        free(result->n_addrs);
                        free(result);
                        return (ND_NOMEM);
                }

                sin = sinbuf;
                for (inaddr = inaddrlist; *inaddr != NULL; inaddr++) {
                        na->len = na->maxlen = sizeof (struct sockaddr_in);
                        na->buf = (char *)sin;
                        sin->sin_family = AF_INET;
                        sin->sin_addr = **inaddr;
                        sin->sin_port = *servp;
                        na++;
                        sin++;
                }
        } else if (af == AF_INET6) {
                sin6buf = calloc(num, sizeof (struct sockaddr_in6));
                if (sin6buf == NULL) {
                        free(result->n_addrs);
                        free(result);
                        return (ND_NOMEM);
                }

                sin6 = sin6buf;
                for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
                        if (IN6_IS_ADDR_V4MAPPED(*in6addr))
                                continue;

                        na->len = na->maxlen = sizeof (struct sockaddr_in6);
                        na->buf = (char *)sin6;
                        sin6->sin6_family = AF_INET6;
                        sin6->sin6_addr = **in6addr;
                        sin6->sin6_port = *servp;
                        na++;
                        sin6++;
                }
        }
        *(nd_alist) = result;
        return (ND_OK);
}

/*
 * Given a hostent and a servent, mallocs and populates
 * a new nd_hostservlist with host and service names.
 *
 * We could be passed in a NULL servent, in which case stringify port.
 */
int
hsents2ndhostservs(struct hostent *he, struct servent *se,
    ushort_t port, struct nd_hostservlist **hslist)
{
        struct  nd_hostservlist *result;
        struct  nd_hostserv *hs;
        int     hosts, servs, i, j;
        char    **hn, **sn;

        if ((result = malloc(sizeof (struct nd_hostservlist))) == 0)
                return (ND_NOMEM);

        /*
         * We initialize the counters to 1 rather than zero because
         * we have to count the "official" name as well as the aliases.
         */
        for (hn = he->h_aliases, hosts = 1; hn && *hn; hn++, hosts++) {};
        if (se) {
                for (sn = se->s_aliases, servs = 1; sn && *sn; sn++, servs++) {
                };
        } else
                servs = 1;

        if ((hs = calloc(hosts * servs, sizeof (struct nd_hostserv))) == 0) {
                free(result);
                return (ND_NOMEM);
        }

        result->h_cnt   = servs * hosts;
        result->h_hostservs = hs;

        for (i = 0, hn = he->h_aliases; i < hosts; i++) {
                sn = se ? se->s_aliases : NULL;

                for (j = 0; j < servs; j++) {
                        if (i == 0)
                                hs->h_host = strdup(he->h_name);
                        else
                                hs->h_host = strdup(*hn);
                        if (j == 0) {
                                if (se)
                                        hs->h_serv = strdup(se->s_name);
                                else {
                                        /* Convert to a number string */
                                        char stmp[16];

                                        (void) sprintf(stmp, "%d", port);
                                        hs->h_serv = strdup(stmp);
                                }
                        } else
                                hs->h_serv = strdup(*sn++);

                        if ((hs->h_host == 0) || (hs->h_serv == 0)) {
                                free(result->h_hostservs);
                                free(result);
                                return (ND_NOMEM);
                        }
                        hs++;
                }
                if (i)
                        hn++;
        }
        *(hslist) = result;
        return (ND_OK);
}

/*
 * Process results from nd_addrlist ( returned by netdir_getbyname)
 * into a hostent using buf.
 * *** ASSUMES that nd_addrlist->n_addrs->buf contains IP addresses in
 * sockaddr_in's ***
 */
int
ndaddr2hent(int af, const char *nam, struct nd_addrlist *addrs,
    struct hostent *result, char *buffer, int buflen)
{
        int     i, count;
        struct  in_addr *addrp;
        struct  in6_addr *addr6p;
        char    **addrvec;
        struct  netbuf *na;
        size_t  len;

        result->h_name          = buffer;
        result->h_addrtype      = af;
        result->h_length        = (af == AF_INET) ? sizeof (*addrp):
            sizeof (*addr6p);

        /*
         * Build addrlist at start of buffer (after name);  store the
         * addresses themselves at the end of the buffer.
         */
        len = strlen(nam) + 1;
        addrvec = (char **)ROUND_UP(buffer + len, sizeof (*addrvec));
        result->h_addr_list     = addrvec;

        if (af == AF_INET) {
                addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
                    sizeof (*addrp));

                count = addrs->n_cnt;
                if ((char *)(&addrvec[count + 1]) > (char *)(&addrp[-count]))
                        return (ND_NOMEM);

                (void) memcpy(buffer, nam, len);

                for (na = addrs->n_addrs, i = 0;  i < count;  na++, i++) {
                        --addrp;
                        (void) memcpy(addrp,
                            /* LINTED pointer cast */
                            &((struct sockaddr_in *)na->buf)->sin_addr,
                            sizeof (*addrp));
                        *addrvec++ = (char *)addrp;
                }
        } else {
                addr6p = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
                    sizeof (*addr6p));

                count = addrs->n_cnt;
                if ((char *)(&addrvec[count + 1]) > (char *)(&addr6p[-count]))
                        return (ND_NOMEM);

                (void) memcpy(buffer, nam, len);

                for (na = addrs->n_addrs, i = 0;  i < count;  na++, i++) {
                        --addr6p;
                        (void) memcpy(addr6p,
                            /* LINTED pointer cast */
                            &((struct sockaddr_in6 *)na->buf)->sin6_addr,
                            sizeof (*addr6p));
                        *addrvec++ = (char *)addr6p;
                }
        }
        *addrvec = 0;
        result->h_aliases = addrvec;

        return (ND_OK);
}

/*
 * Process results from nd_addrlist ( returned by netdir_getbyname)
 * into a servent using buf.
 */
int
ndaddr2srent(const char *name, const char *proto, ushort_t port,
    struct servent *result, char *buffer, int buflen)
{
        size_t  i;
        char    *bufend = (buffer + buflen);

        result->s_port = (int)port;

        result->s_aliases =
            (char **)ROUND_UP(buffer, sizeof (char *));
        result->s_aliases[0] = NULL;
        buffer = (char *)&result->s_aliases[1];
        result->s_name = buffer;
        i = strlen(name) + 1;
        if ((buffer + i) > bufend)
                return (ND_NOMEM);
        (void) memcpy(buffer, name, i);
        buffer += i;

        result->s_proto = buffer;
        i = strlen(proto) + 1;
        if ((buffer + i) > bufend)
                return (ND_NOMEM);
        (void) memcpy(buffer, proto, i);
        buffer += i;

        return (ND_OK);
}

/*
 * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
 * into a hostent using buf.
 * *** ASSUMES that nd_buf->buf is a sockaddr_in ***
 */
int
ndhostserv2hent(struct netbuf *nbuf, struct nd_hostservlist *addrs,
    struct hostent *result, char *buffer, int buflen)
{
        int     i, count;
        char    *aliasp;
        char    **aliasvec;
        struct  sockaddr_in *sa;
        struct  nd_hostserv *hs;
        const   char *la;
        size_t  length;

        /* First, give the lonely address a specious home in h_addr_list. */
        aliasp   = (char  *)ROUND_UP(buffer, sizeof (sa->sin_addr));
        /* LINTED pointer cast */
        sa = (struct sockaddr_in *)nbuf->buf;
        (void) memcpy(aliasp, &(sa->sin_addr), sizeof (sa->sin_addr));
        aliasvec = (char **)ROUND_UP(aliasp + sizeof (sa->sin_addr),
            sizeof (*aliasvec));
        result->h_addr_list = aliasvec;
        *aliasvec++ = aliasp;
        *aliasvec++ = 0;

        /*
         * Build h_aliases at start of buffer (after addr and h_addr_list);
         * store the alias strings at the end of the buffer (before h_name).
         */

        aliasp = buffer + buflen;

        result->h_aliases       = aliasvec;

        hs = addrs->h_hostservs;
        if (!hs)
                return (ND_NOHOST);

        length = strlen(hs->h_host) + 1;
        aliasp -= length;
        if ((char *)(&aliasvec[1]) > aliasp)
                return (ND_NOMEM);
        (void) memcpy(aliasp, hs->h_host, length);

        result->h_name          = aliasp;
        result->h_addrtype      = AF_INET;
        result->h_length        = sizeof (sa->sin_addr);

        /*
         * Assumption: the netdir nametoaddr_libs
         * sort the vector of (host, serv) pairs in such a way that
         * all pairs with the same host name are contiguous.
         */
        la = hs->h_host;
        count = addrs->h_cnt;
        for (i = 0;  i < count;  i++, hs++)
                if (strcmp(la, hs->h_host) != 0) {
                        size_t len = strlen(hs->h_host) + 1;

                        aliasp -= len;
                        if ((char *)(&aliasvec[2]) > aliasp)
                                return (ND_NOMEM);
                        (void) memcpy(aliasp, hs->h_host, len);
                        *aliasvec++ = aliasp;
                        la = hs->h_host;
                }
        *aliasvec = 0;

        return (ND_OK);
}

/*
 * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
 * into a servent using buf.
 */
int
ndhostserv2srent(int port, const char *proto, struct nd_hostservlist *addrs,
    struct servent *result, char *buffer, int buflen)
{
        int     i, count;
        char    *aliasp;
        char    **aliasvec;
        struct  nd_hostserv *hs;
        const   char *host_cname;
        size_t  leni, lenj;

        result->s_port = port;
        /*
         * Build s_aliases at start of buffer;
         * store proto and aliases at the end of the buffer (before h_name).
         */

        aliasp = buffer + buflen;
        aliasvec = (char **)ROUND_UP(buffer, sizeof (char *));

        result->s_aliases       = aliasvec;

        hs = addrs->h_hostservs;
        if (!hs)
                return (ND_NOHOST);
        host_cname = hs->h_host;

        leni = strlen(proto) + 1;
        lenj = strlen(hs->h_serv) + 1;
        if ((char *)(&aliasvec[2]) > (aliasp - leni - lenj))
                return (ND_NOMEM);

        aliasp -= leni;
        (void) memcpy(aliasp, proto, leni);
        result->s_proto = aliasp;

        aliasp -= lenj;
        (void) memcpy(aliasp, hs->h_serv, lenj);
        result->s_name = aliasp;

        /*
         * Assumption: the netdir nametoaddr_libs
         * do a host aliases first and serv aliases next
         * enumeration for creating the list of hostserv
         * structures.
         */
        count = addrs->h_cnt;
        for (i = 0;
            i < count && hs->h_serv && strcmp(hs->h_host, host_cname) == 0;
            i++, hs++) {
                size_t len = strlen(hs->h_serv) + 1;

                aliasp -= len;
                if ((char *)(&aliasvec[2]) > aliasp)
                        return (ND_NOMEM);
                (void) memcpy(aliasp, hs->h_serv, len);
                *aliasvec++ = aliasp;
        }
        *aliasvec = NULL;

        return (ND_OK);
}


static int
nd2herrno(int nerr)
{
        switch (nerr) {
        case ND_OK:
                return (0);
        case ND_TRY_AGAIN:
                return (TRY_AGAIN);
        case ND_NO_RECOVERY:
        case ND_BADARG:
        case ND_NOMEM:
                return (NO_RECOVERY);
        case ND_NO_DATA:
                return (NO_DATA);
        case ND_NOHOST:
        case ND_NOSERV:
                return (HOST_NOT_FOUND);
        default:
                return (NO_RECOVERY);
        }
}

/*
 * This is a utility function so that various parts of libnsl can
 * easily send ioctls down to ip.
 *
 */
int
nss_ioctl(int af, int cmd, void *arg)
{
        int     fd;
        char    *devpath;
        int     retv;

        switch (af) {
        case AF_INET6:
                devpath = UDP6DEV;
                break;
        case AF_INET:
        case AF_UNSPEC:
        default:
                devpath = UDPDEV;
        }
        if ((fd = open(devpath, O_RDONLY)) < 0) {
                return (-1);
        }
        while ((retv = ioctl(fd, cmd, arg)) == -1) {
                if (errno != EINTR)
        break;
        }
        (void) close(fd);
        return (retv);
}

static int
nss_strioctl(int af, int cmd, void *ptr, int ilen)
{
        struct strioctl str;

        str.ic_cmd = cmd;
        str.ic_timout = 0;
        str.ic_len = ilen;
        str.ic_dp = ptr;

        return (nss_ioctl(af, I_STR, &str));
}

static struct ifinfo *
get_local_info(void)
{
        int     numifs;
        int     n;
        char    *buf = NULL;
        size_t  needed;
        struct lifconf  lifc;
        struct lifreq   lifreq, *lifr;
        struct lifnum   lifn;
        struct ifinfo   *localinfo;

        lifn.lifn_family = AF_UNSPEC;
        lifn.lifn_flags = 0;

getifnum:
        if (nss_ioctl(AF_UNSPEC, SIOCGLIFNUM, &lifn) == -1) {
                numifs = MAXIFS;
        } else {
                numifs = lifn.lifn_count;
        }

        /*
         * Add a small fudge factor in case interfaces get plumbed between
         * the call to SIOCGLIFNUM and SIOCGLIFCONF.
         */
        needed = (numifs + 4) * sizeof (lifreq);
        if (buf == NULL)
                buf = malloc(needed);
        else
                buf = realloc(buf, needed);
        if (buf == NULL) {
                (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
                _nderror = ND_NOMEM;
                return (NULL);
        }
        lifc.lifc_family = AF_UNSPEC;
        lifc.lifc_flags = 0;
        lifc.lifc_len = needed;
        lifc.lifc_buf = buf;
        if (nss_ioctl(AF_UNSPEC, SIOCGLIFCONF, &lifc) == -1) {
                /*
                 * IP returns EINVAL if the buffer was too small to fit
                 * all of the entries.  If that's the case, go back and
                 * try again.
                 */
                if (errno == EINVAL)
                        goto getifnum;

                (void) syslog(LOG_ERR, "n2a get_local_info: "
                    "ioctl (get interface configuration): %m");
                free(buf);
                _nderror = ND_SYSTEM;
                return (NULL);
        }
        /* LINTED pointer cast */
        lifr = (struct lifreq *)buf;
        numifs = lifc.lifc_len/sizeof (lifreq);
        localinfo = malloc(ifinfosize(numifs));
        if (localinfo == NULL) {
                (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
                free(buf);
                _nderror = ND_SYSTEM;
                return (NULL);
        }

        /* LINTED pointer cast */
        localinfo->addresses = (struct __ifaddr *)
            ((char *)localinfo + sizeof (struct ifinfo));

        for (localinfo->count = 0, n = numifs; n > 0; n--, lifr++) {
                int af;

                lifreq = *lifr;
                af = lifreq.lifr_addr.ss_family;

                /* Squirrel away the address */
                if (ifassign(lifreq, localinfo->count, IF_ADDR) == 0)
                        continue;

                if (nss_ioctl(af, SIOCGLIFFLAGS, &lifreq) < 0) {
                        (void) syslog(LOG_ERR,
                            "n2a get_local_info: "
                            "ioctl (get interface flags): %m");
                        continue;
                }
                if (!(lifreq.lifr_flags & IFF_UP))
                        continue;

                if (nss_ioctl(af, SIOCGLIFNETMASK, &lifreq) < 0) {
                        (void) syslog(LOG_ERR,
                            "n2a get_local_info: "
                            "ioctl (get interface netmask): %m");
                        continue;
                }

                if (ifassign(lifreq, localinfo->count, IF_MASK) == 0)
                        continue;

                localinfo->count++;
        }

        free(buf);
        return (localinfo);
}

static int
__inet_ifassign(sa_family_t af, struct __ifaddr *ifa, __ifaddr_type type,
    void *addr) {
        switch (type) {
        case IF_ADDR:
                ifa->af = af;
                if (af == AF_INET6) {
                        ifa->addr.in6 = *(struct in6_addr *)addr;
                } else {
                        ifa->addr.in4 = *(struct in_addr *)addr;
                }
                break;
        case IF_MASK:
                if (ifa->af == af) {
                        if (af == AF_INET6) {
                                ifa->mask.in6 = *(struct in6_addr *)addr;
                        } else {
                                ifa->mask.in4 = *(struct in_addr *)addr;
                        }
                } else {
                        return (0);
                }
                break;
        default:
                return (0);
        }

        return (1);
}

/*
 *  Some higher-level routines for determining if an address is
 *  on a local network.
 *
 *      __inet_get_local_interfaces() - get an opaque handle with
 *          with a list of local interfaces
 *      __inet_address_is_local() - return 1 if an address is
 *          on a local network; 0 otherwise
 *      __inet_free_local_interfaces() - free handle that was
 *          returned by __inet_get_local_interfaces()
 *
 *  A typical calling sequence is:
 *
 *      p = __inet_get_local_interfaces();
 *      if (__inet_address_is_local(p, inaddr)) {
 *          ...
 *      }
 *      __inet_free_local_interfaces(p);
 */

/*
 *  Return an opaque pointer to a list of configured interfaces.
 */
void *
__inet_get_local_interfaces(void)
{
        return (get_local_info());
}

/*
 *  Free memory allocated by inet_local_interfaces().
 */
void
__inet_free_local_interfaces(void *p)
{
        free(p);
}

/*
 *  Determine if an address is on a local network.
 *
 *  Might have made sense to use SIOCTONLINK, except that it doesn't
 *  handle matching on IPv4 network addresses.
 */
int
__inet_address_is_local_af(void *p, sa_family_t af, void *addr) {

        struct ifinfo   *localinfo = (struct ifinfo *)p;
        int             i, a;
        struct in_addr  v4addr;

        if (localinfo == 0)
                return (0);

        if (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr)) {
                IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addr, &v4addr);
                af = AF_INET;
                addr = (void *)&v4addr;
        }

        for (i = 0; i < localinfo->count; i++) {
                if (ifaf(i) == af) {
                        if (af == AF_INET6) {
                                struct in6_addr *a6 = (struct in6_addr *)addr;
                                for (a = 0; a < sizeof (a6->s6_addr); a++) {
                                        if ((a6->s6_addr[a] &
                                                ifmask6(i).s6_addr[a]) !=
                                                (ifaddr6(i).s6_addr[a] &
                                                ifmask6(i).s6_addr[a]))
                                                break;
                                }
                                if (a >= sizeof (a6->s6_addr))
                                        return (1);
                        } else {
                                if ((((struct in_addr *)addr)->s_addr &
                                                ifmask4(i).s_addr) ==
                                        (ifaddr4(i).s_addr &
                                                ifmask4(i).s_addr))
                                        return (1);
                        }
                }
        }

        return (0);
}

int
__inet_address_is_local(void *p, struct in_addr addr)
{
        return (__inet_address_is_local_af(p, AF_INET, &addr));
}

int
__inet_uaddr_is_local(void *p, struct netconfig *nc, char *uaddr)
{
        struct netbuf           *taddr;
        sa_family_t             af;
        int                     ret;

        taddr = uaddr2taddr(nc, uaddr);
        if (taddr == 0)
                return (0);

        /* LINTED pointer cast */
        af = ((struct sockaddr *)taddr->buf)->sa_family;

        ret = __inet_address_is_local_af(p, af, (af == AF_INET6) ?
            /* LINTED pointer cast */
            (void *)&((struct sockaddr_in6 *)taddr->buf)->sin6_addr :
            /* LINTED pointer cast */
            (void *)&((struct sockaddr_in *)taddr->buf)->sin_addr);

        netdir_free(taddr, ND_ADDR);
        return (ret);
}


int
__inet_address_count(void *p)
{
        struct ifinfo *lp = (struct ifinfo *)p;

        if (lp != 0) {
                return (lp->count);
        } else {
                return (0);
        }
}

uint32_t
__inet_get_addr(void *p, int n)
{
        struct ifinfo *localinfo = (struct ifinfo *)p;

        if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
                return (0);

        return (ifaddr4(n).s_addr);
}

uint32_t
__inet_get_network(void *p, int n)
{
        struct ifinfo *localinfo = (struct ifinfo *)p;

        if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
                return (0);

        return (ifaddr4(n).s_addr & ifmask4(n).s_addr);
}

char *
__inet_get_uaddr(void *p, struct netconfig *nc, int n)
{
        struct ifinfo *localinfo = (struct ifinfo *)p;
        char *uaddr;
        struct sockaddr_in sin4;
        struct sockaddr_in6 sin6;
        struct netbuf nb;

        if (localinfo == 0 || nc == 0 || n >= localinfo->count)
                return (0);

        if (ifaf(n) == AF_INET6) {
                if (strcmp(NC_INET6, nc->nc_protofmly) != 0)
                        return (0);
                (void) memset(&sin6, 0, sizeof (sin6));
                sin6.sin6_family = AF_INET6;
                sin6.sin6_addr = ifaddr6(n);
                nb.buf = (char *)&sin6;
                nb.len = sizeof (sin6);
        } else {
                if (strcmp(NC_INET, nc->nc_protofmly) != 0)
                        return (0);
                (void) memset(&sin4, 0, sizeof (sin4));
                sin4.sin_family = AF_INET;
                sin4.sin_addr = ifaddr4(n);
                nb.buf = (char *)&sin4;
                nb.len = sizeof (sin4);
        }

        nb.maxlen = nb.len;

        uaddr = taddr2uaddr(nc, &nb);
        return (uaddr);
}

char *
__inet_get_networka(void *p, int n)
{
        struct ifinfo   *localinfo = (struct ifinfo *)p;

        if (localinfo == 0 || n >= localinfo->count)
                return (0);

        if (ifaf(n) == AF_INET6) {
                char            buf[INET6_ADDRSTRLEN];
                struct in6_addr in6;
                int             i;

                for (i = 0; i < sizeof (in6.s6_addr); i++) {
                        in6.s6_addr[i] = ifaddr6(n).s6_addr[i] &
                            ifmask6(n).s6_addr[i];
                }
                return (strdup(inet_ntop(AF_INET6, &in6, buf, sizeof (buf))));
        } else {
                struct in_addr  in4;

                in4.s_addr = ifaddr4(n).s_addr & ifmask4(n).s_addr;
                return (strdup(inet_ntoa(in4)));
        }
}

static int
in_list(struct in_addr *addrs, int n, struct in_addr a)
{
        int i;

        for (i = 0; i < n; i++) {
                if (addrs[i].s_addr == a.s_addr)
                        return (1);
        }
        return (0);
}

static int
getbroadcastnets(struct netconfig *tp, struct in_addr **addrs)
{
        struct ifconf ifc;
        struct ifreq ifreq, *ifr;
        struct sockaddr_in *sin;
        struct in_addr a;
        int fd;
        int n, i, numifs;
        char *buf;
        int     use_loopback = 0;

        _nderror = ND_SYSTEM;
        fd = open(tp->nc_device, O_RDONLY);
        if (fd < 0) {
                (void) syslog(LOG_ERR,
            "broadcast: open to get interface configuration: %m");
                return (0);
        }
        if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0)
                numifs = MAXIFS;
        buf = malloc(numifs * sizeof (struct ifreq));
        if (buf == NULL) {
                (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
                (void) close(fd);
                return (0);
        }
        *addrs = malloc(numifs * sizeof (struct in_addr));
        if (*addrs == NULL) {
                (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
                free(buf);
                (void) close(fd);
                return (0);
        }
        ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
        ifc.ifc_buf = buf;
        /*
         * Ideally, this ioctl should also tell me, how many bytes were
         * finally allocated, but it doesnt.
         */
        if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
                (void) syslog(LOG_ERR,
            "broadcast: ioctl (get interface configuration): %m");
                free(buf);
                free(*addrs);
                (void) close(fd);
                return (0);
        }

retry:
        /* LINTED pointer cast */
        ifr = (struct ifreq *)buf;
        for (i = 0, n = ifc.ifc_len / (int)sizeof (struct ifreq);
            n > 0; n--, ifr++) {
                ifreq = *ifr;
                if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
                        (void) syslog(LOG_ERR, "broadcast: "
                            "ioctl (get interface flags): %m");
                        continue;
                }
                if (!(ifreq.ifr_flags & IFF_UP) ||
                    (ifr->ifr_addr.sa_family != AF_INET))
                        continue;
                if (ifreq.ifr_flags & IFF_BROADCAST) {
                        /* LINTED pointer cast */
                        sin = (struct sockaddr_in *)&ifr->ifr_addr;
                        if (ioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
                                /* May not work with other implementation */
                                a = _inet_makeaddr(
                                    inet_netof(sin->sin_addr),
                                    INADDR_ANY);
                                if (!in_list(*addrs, i, a))
                                        (*addrs)[i++] = a;
                        } else {
                                /* LINTED pointer cast */
                                a = ((struct sockaddr_in *)
                                    &ifreq.ifr_addr)->sin_addr;
                                if (!in_list(*addrs, i, a))
                                        (*addrs)[i++] = a;
                        }
                        continue;
                }
                if (use_loopback && (ifreq.ifr_flags & IFF_LOOPBACK)) {
                        /* LINTED pointer cast */
                        sin = (struct sockaddr_in *)&ifr->ifr_addr;
                        a = sin->sin_addr;
                        if (!in_list(*addrs, i, a))
                                (*addrs)[i++] = a;
                        continue;
                }
                if (ifreq.ifr_flags & IFF_POINTOPOINT) {
                        if (ioctl(fd, SIOCGIFDSTADDR, (char *)&ifreq) < 0)
                                continue;
                        /* LINTED pointer cast */
                        a = ((struct sockaddr_in *)
                            &ifreq.ifr_addr)->sin_addr;
                        if (!in_list(*addrs, i, a))
                                (*addrs)[i++] = a;
                        continue;
                }
        }
        if (i == 0 && !use_loopback) {
                use_loopback = 1;
                goto retry;
        }
        free(buf);
        (void) close(fd);
        if (i)
                _nderror = ND_OK;
        else
                free(*addrs);
        return (i);
}

/*
 * This is lifted straight from libsocket/inet/inet_mkaddr.c.
 * Copied here to avoid our dependency on libsocket. More importantly,
 * to make sure partially static apps that use libnsl, but not
 * libsocket, don't get screwed up.
 * If you understand the above paragraph, try to get rid of
 * this copy of inet_makeaddr; if you don;t, leave it alone.
 *
 * Formulate an Internet address from network + host.  Used in
 * building addresses stored in the ifnet structure.
 */
static struct in_addr
_inet_makeaddr(in_addr_t net, in_addr_t host)
{
        in_addr_t addr;
        struct in_addr inaddr;

        if (net < 128)
                addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST);
        else if (net < 65536)
                addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST);
        else if (net < 16777216L)
                addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST);
        else
                addr = net | host;
        inaddr.s_addr = htonl(addr);
        return (inaddr);
}

/*
 * Routine to read the default configuration file and check if SORT_ADDRS
 * is set to NO or FALSE. This routine is called by order_haddrlist_af()
 * to determine if the addresses need to be sorted.
 */
static boolean_t
_read_nsw_file(void)
{
        char    defval[LINESIZE];
        FILE    *defl;
        boolean_t       nosort = B_FALSE;


        do {
                defl = fopen(__NSW_DEFAULT_FILE, "rF");
        } while ((defl == NULL) && (errno == EINTR));

        if (defl == NULL)
                return (B_FALSE);

        while (fgets(defval, sizeof (defval), defl) != NULL) {
                if ((strncmp(DONT_SORT, defval, sizeof (DONT_SORT) - 1) == 0) ||
                    (strncmp(DONT_SORT2, defval,
                    sizeof (DONT_SORT2) - 1) == 0)) {
                        nosort = B_TRUE;
                        break;
                }
        }
        (void) fclose(defl);
        return (nosort);
}