summaryrefslogtreecommitdiff
path: root/usr/src/lib/libnsl/nss/netdir_inet.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libnsl/nss/netdir_inet.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libnsl/nss/netdir_inet.c')
-rw-r--r--usr/src/lib/libnsl/nss/netdir_inet.c3094
1 files changed, 3094 insertions, 0 deletions
diff --git a/usr/src/lib/libnsl/nss/netdir_inet.c b/usr/src/lib/libnsl/nss/netdir_inet.c
new file mode 100644
index 0000000000..cc9ceaf809
--- /dev/null
+++ b/usr/src/lib/libnsl/nss/netdir_inet.c
@@ -0,0 +1,3094 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * lib/libnsl/nss/netdir_inet.c
+ *
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 <rpc/trace.h>
+#include <syslog.h>
+#include <nsswitch.h>
+#include "nss.h"
+#include "nsl_stdio_prv.h"
+
+#define MAXIFS 32
+#define UDPDEV "/dev/udp"
+#define UDP6DEV "/dev/udp6"
+
+#define GETHOSTBUF(host_buf) \
+ NSS_XbyY_ALLOC(&host_buf, sizeof (struct hostent), NSS_BUFLEN_HOSTS)
+#define GETSERVBUF(serv_buf) \
+ NSS_XbyY_ALLOC(&serv_buf, sizeof (struct servent), NSS_BUFLEN_SERVICES)
+
+#ifdef PIC
+#define DOOR_GETHOSTBYNAME_R _door_gethostbyname_r
+#define DOOR_GETHOSTBYADDR_R _door_gethostbyaddr_r
+#define DOOR_GETIPNODEBYNAME_R _door_getipnodebyname_r
+#define DOOR_GETIPNODEBYADDR_R _door_getipnodebyaddr_r
+#else
+#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
+#endif /* PIC */
+
+#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 islocal();
+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.
+ */
+
+/*
+ * 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;
+ extern int _inet_aton();
+
+
+ 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) {
+
+ 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);
+ 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)) {
+ mutex_lock(&nd_addr_lock);
+ (void) memset((char *)&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);
+ 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;
+
+ mutex_lock(&nd6_addr_lock);
+ (void) memset((char *)&sa6_con, 0, sizeof (sa6_con));
+ sa6_con.sin6_family = AF_INET6;
+ memcpy((char *)&(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);
+ 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
+ * FREE_return()) 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.
+ */
+#define FREE_return(ret) \
+ { \
+ if (inaddrs) \
+ free(inaddrs); \
+ if (baddrlist) \
+ free(baddrlist); \
+ _nderror = ret; \
+ return (ret); \
+ }
+ int i, bnets;
+
+ bnets = getbroadcastnets(nconf, &inaddrs);
+ if (bnets == 0) {
+ _nderror = ND_NOHOST;
+ return (ND_NOHOST);
+ }
+ baddrlist =
+ (char **)malloc((bnets+1)*sizeof (char *));
+ if (baddrlist == NULL)
+ FREE_return(ND_NOMEM);
+ 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);
+ FREE_return(ret)
+ }
+ 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);
+ FREE_return(ret)
+ }
+ 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) {
+ trace1(TR__get_hostserv_inetnetdir_byname, 12);
+ 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 ?
+ */
+ GETSERVBUF(ndbuf4switch);
+ if (ndbuf4switch == 0)
+ FREE_return(ND_NOMEM);
+ se = _switch_getservbyname_r(args->arg.nd_hs->h_serv,
+ proto, ndbuf4switch->result,
+ ndbuf4switch->buffer, ndbuf4switch->buflen);
+ if (!se) {
+ NSS_XbyY_FREE(&ndbuf4switch);
+ FREE_return(ND_NOSERV)
+ }
+ server_port = se->s_port;
+ NSS_XbyY_FREE(&ndbuf4switch);
+ }
+
+ if (haddrlist == 0) {
+ int h_errnop = 0;
+
+ GETHOSTBUF(ndbuf4switch);
+ 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 = h_errnop ?
+ __herrno2netdir(h_errnop) :
+ ND_NOHOST;
+ 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);
+ FREE_return(ret)
+ }
+
+
+ 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 ?
+ */
+ GETSERVBUF(ndbuf4switch);
+ if (ndbuf4switch == 0)
+ FREE_return(ND_NOMEM);
+ se = _switch_getservbyname_r(
+ args->arg.nd_hs->h_serv,
+ proto, ndbuf4switch->result,
+ ndbuf4switch->buffer, ndbuf4switch->buflen);
+ if (!se) {
+ NSS_XbyY_FREE(&ndbuf4switch);
+ FREE_return(ND_NOSERV)
+ }
+ server_port = se->s_port;
+ NSS_XbyY_FREE(&ndbuf4switch);
+ }
+
+ if (haddrlist == 0) {
+ int h_errnop = 0;
+
+ GETHOSTBUF(ndbuf4switch);
+ 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 = h_errnop ?
+ __herrno2netdir(h_errnop) :
+ ND_NOHOST;
+ 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);
+ FREE_return(ret)
+ }
+
+ 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,
+ ((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".
+ */
+ if (*(uint32_t *)(args->arg.nss.host.addr) ==
+ htonl(INADDR_LOOPBACK)) {
+ 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);
+ 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.
+ */
+ 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.
+ */
+ 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:
+
+ GETSERVBUF(ndbuf4serv);
+ if (ndbuf4serv == 0) {
+ _nderror = ND_NOMEM;
+ return (_nderror);
+ }
+ 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.
+ */
+ }
+ }
+
+ GETHOSTBUF(ndbuf4host);
+ 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:
+
+ GETSERVBUF(ndbuf4serv);
+ if (ndbuf4serv == 0) {
+ _nderror = ND_NOMEM;
+ return (ND_NOMEM);
+ }
+ 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.
+ */
+ }
+ }
+
+ GETHOSTBUF(ndbuf4host);
+ 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:
+
+ 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.
+ */
+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);
+ }
+ /* NOTREACHED */
+ return (0); /* keep gcc happy */
+}
+
+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;
+
+ trace2(TR__switch_gethostbyname_r, 0, buflen);
+ 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;
+ *h_errnop = arg.h_errno;
+ if (arg.returnval != NULL)
+ order_haddrlist_af(result->h_addrtype, result->h_addr_list);
+ trace2(TR__switch_gethostbyname_r, 1, buflen);
+ 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;
+
+ trace2(TR__switch_getipnodebyname_r, 0, buflen);
+ 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;
+ *h_errnop = arg.h_errno;
+ if (arg.returnval != NULL)
+ order_haddrlist_af(result->h_addrtype, result->h_addr_list);
+ trace2(TR__switch_getipnodebyname_r, 1, buflen);
+ 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;
+
+ trace3(TR__switch_gethostbyaddr_r, 0, len, buflen);
+ 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;
+ *h_errnop = arg.h_errno;
+ trace3(TR__switch_gethostbyaddr_r, 1, len, buflen);
+ 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;
+
+ trace3(TR__switch_getipnodebyaddr_r, 0, len, buflen);
+ 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;
+ *h_errnop = arg.h_errno;
+ trace3(TR__switch_getipnodebyaddr_r, 1, len, buflen);
+ return (struct hostent *)NSS_XbyY_FINI(&arg);
+}
+
+static void
+_nss_initf_services(nss_db_params_t *p)
+{
+ /* === need tracepoints */
+ 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;
+ sorted = (struct in_addr **)sortbuf;
+ 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.
+ */
+ 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;
+ int index;
+ 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 = (struct nd_hostservlist *)
+ 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 = (struct nd_hostserv *)calloc(hosts * servs,
+ sizeof (struct nd_hostserv))) == 0) {
+ free((void *)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((void *)result->h_hostservs);
+ free((void *)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,
+ &((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,
+ &((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));
+ sa = (struct sockaddr_in *)nbuf->buf;
+ (void) memcpy(aliasp, (char *)&(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)
+{
+ trace1(TR_nd2herrno, 0);
+ switch (nerr) {
+ case ND_OK:
+ trace1(TR_nd2herrno, 1);
+ return (0);
+ case ND_TRY_AGAIN:
+ trace1(TR_nd2herrno, 1);
+ return (TRY_AGAIN);
+ case ND_NO_RECOVERY:
+ case ND_BADARG:
+ case ND_NOMEM:
+ trace1(TR_nd2herrno, 1);
+ return (NO_RECOVERY);
+ case ND_NO_DATA:
+ trace1(TR_nd2herrno, 1);
+ return (NO_DATA);
+ case ND_NOHOST:
+ case ND_NOSERV:
+ trace1(TR_nd2herrno, 1);
+ return (HOST_NOT_FOUND);
+ default:
+ trace1(TR_nd2herrno, 1);
+ 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;
+ }
+ 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);
+ }
+ lifr = (struct lifreq *)buf;
+ numifs = lifc.lifc_len/sizeof (lifreq);
+ localinfo = (struct ifinfo *)malloc(ifinfosize(numifs));
+ if (localinfo == NULL) {
+ (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
+ free(buf);
+ _nderror = ND_SYSTEM;
+ return (NULL);
+ }
+
+ 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);
+}
+
+static int
+islocal(struct ifinfo *localinfo, struct in_addr addr)
+{
+ int i;
+
+ if (!localinfo)
+ return (0);
+
+ for (i = 0; i < localinfo->count; i++) {
+ if (ifaf(i) == AF_INET &&
+ ((addr.s_addr & ifmask4(i).s_addr) ==
+ (ifaddr4(i).s_addr & ifmask4(i).s_addr)))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * 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);
+
+ af = ((struct sockaddr *)taddr->buf)->sa_family;
+
+ ret = __inet_address_is_local_af(p, af,
+ (af == AF_INET6) ?
+ (void *)&((struct sockaddr_in6 *)taddr->buf)->sin6_addr :
+ (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 sockaddr *sin;
+ 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);
+ 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);
+ 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 = (char *)malloc(numifs * sizeof (struct ifreq));
+ if (buf == NULL) {
+ (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
+ (void) close(fd);
+ return (0);
+ }
+ *addrs = (struct in_addr *)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:
+ 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) {
+ 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 {
+ 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)) {
+ 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;
+ 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];
+ __NSL_FILE *defl;
+ boolean_t nosort = B_FALSE;
+
+
+ do {
+ defl = __nsl_fopen(__NSW_DEFAULT_FILE, "r");
+ } while ((defl == NULL) && (errno == EINTR));
+
+ if (defl == NULL)
+ return (B_FALSE);
+
+ while (__nsl_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;
+ }
+ }
+ __nsl_fclose(defl);
+ return (nosort);
+}