netdir_inet.c revision d81a47b31915db183b6865cc617bb95aa37df2f4
/*
* 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
* 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 "-"
* therefore, /etc/nsswitch.conf is effectively the only place that
* If an administrator chooses to bypass the name service switch by
* implementation does NOT call the name service switch, it merely loops
* through the nametoaddr libs. In this case, if this code was called
* transport independent netbuf or hostserv, and unmarshal the resulting
* nd_addrlist or hostservlist back into hostent and servent, as the case
* may be.
*
* 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/byteorder.h>
#include <errno.h>
#include <fcntl.h>
#include <thread.h>
#include <synch.h>
#include <netdb.h>
#include <netconfig.h>
#include <netdir.h>
#include <tiuser.h>
#include <nss_dbdefs.h>
#include <nss_netdir.h>
#include <syslog.h>
#include <nsswitch.h>
#include "nss.h"
#define MAXIFS 32
#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 *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 struct sockaddr_in sa_con;
/* IPv6 nd_addrlist */
static struct sockaddr_in6 sa6_con;
#define LOCALHOST "localhost"
struct servent *_switch_getservbyname_r(const char *, const char *,
struct servent *, char *, int);
char *, int);
static int __herrno2netdir(int h_errnop);
static struct ifinfo *get_local_info(void);
static int hent2ndaddr(int, char **, int *, struct nd_addrlist **);
static int ndaddr2hent(int, const char *, struct nd_addrlist *,
struct hostent *, char *, int);
struct nd_hostservlist **);
char *, int);
struct hostent *, char *, int);
static int ndhostserv2srent(int, const char *, struct nd_hostservlist *,
struct servent *, char *, int);
static int dstcmp(const void *, const void *);
static boolean_t _read_nsw_file(void);
/*
* Begin: PART I
*/
static int
{
if (inaddrs)
if (baddrlist)
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
{
int server_port;
int *servp = &server_port;
char **haddrlist;
char *dotnamelist[2];
struct in6_addr v6nameaddr;
return (ND_BADARG);
}
/*
* 1. gethostbyname()/netdir_getbyname() special cases:
*/
case NSS_HOST:
/*
* Worth the performance gain -- assuming a lot of inet apps
* actively use "localhost".
*/
(void) mutex_lock(&nd_addr_lock);
(void) mutex_unlock(&nd_addr_lock);
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.
*/
(struct in_addr *)&dotnameaddr)) {
(void) mutex_lock(&nd_addr_lock);
(void) mutex_unlock(&nd_addr_lock);
return (_nderror);
}
break;
case NSS_HOST6:
/*
* Handle case of literal address string.
*/
&v6nameaddr) != 0)) {
int ret;
(void) mutex_lock(&nd6_addr_lock);
&v6nameaddr, sizeof (struct in6_addr));
(void) mutex_unlock(&nd6_addr_lock);
else
return (ret);
}
break;
case NETDIR_BY:
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.
*/
"rpcbind") == 0) {
"0123456789") ==
} else {
/* i.e. need to call a name service on this */
}
/*
* 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.
*/
return (ND_NOHOST);
HOST_SELF_BIND) == 0)) {
HOST_SELF_CONNECT) == 0)) {
LOCALHOST) == 0)) {
} else if ((int)(dotnameaddr =
/*
* If the caller passed in a dot separated IP
* notation to netdir_getbyname, convert that
* back into address.
*/
dotnamelist[0] = (char *)&dotnameaddr;
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;
if (bnets == 0) {
return (ND_NOHOST);
}
baddrlist));
for (i = 0; i < bnets; i++)
} else {
/* i.e. need to call a name service on this */
haddrlist = 0;
}
int ret;
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done, freed using
* netdir_free.
*/
}
break;
case NETDIR_BY6:
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.
*/
"rpcbind") == 0) {
} else {
/* i.e. need to call a name service on this */
}
/*
* 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.
*/
return (ND_NOHOST);
HOST_SELF_BIND) == 0)) {
HOST_SELF_CONNECT) == 0)) {
LOCALHOST) == 0)) {
!= NULL) {
/*
* If the caller passed in a dot separated IP notation
* to netdir_getbyname, convert that back into address.
*/
&v6nameaddr)) != 0) {
dotnamelist[0] = (char *)&v6nameaddr;
}
else
/* not sure what to return */
return (ND_NOHOST);
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;
}
int ret;
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done, freed
* using netdir_free.
*/
}
break;
}
/*
* Emphasis on improving performance in the "if" part.
*/
if (nconf->nc_nlookups == 0) {
int ret;
nss_XbyY_buf_t *ndbuf4switch = 0;
case NSS_HOST:
case NSS_HOST6:
case NSS_SERV:
if (se == 0)
return (_nderror);
case NETDIR_BY:
if (servp == 0) {
/*
* 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 ?
*/
sizeof (struct servent), NSS_BUFLEN_SERVICES);
if (ndbuf4switch == 0)
baddrlist));
if (!se) {
baddrlist));
}
}
if (haddrlist == 0) {
int h_errnop = 0;
sizeof (struct hostent),
if (ndbuf4switch == 0) {
return (ND_NOMEM);
}
/*
* Search the ipnodes (v6) path first,
* search will return the v4 addresses
* as v4mapped addresses.
*/
if ((tmphe = DOOR_GETIPNODEBYNAME_R(
/* Failover case, try hosts db for v4 address */
return (_nderror);
}
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done, freed using
* netdir_free.
*/
} else {
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done, freed using
* netdir_free.
*/
}
return (ret);
} else {
int ret;
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done, freed using netdir_free.
*/
}
case NETDIR_BY6:
if (servp == 0) {
/*
* 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 ?
*/
sizeof (struct servent),
if (ndbuf4switch == 0)
baddrlist));
if (!se) {
baddrlist));
}
}
if (haddrlist == 0) {
int h_errnop = 0;
sizeof (struct hostent),
if (ndbuf4switch == 0) {
return (ND_NOMEM);
}
return (_nderror);
}
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done,
* freed using netdir_free.
*/
((struct hostent *)
return (ret);
} else {
int ret;
/*
* Convert h_addr_list into nd_addrlist.
* malloc's will be done,
* freed using netdir_free.
*/
}
default:
return (ND_BADARG); /* should never happen */
}
} else {
/* haddrlist is no longer used, so clean up */
if (inaddrs)
if (baddrlist)
}
/*
* 3. We come this far only if nametoaddr libs are specified for
* inet transports and we are called by gethost/servbyname only.
*/
struct nd_hostserv service;
struct nd_addrlist *addrs;
int ret;
case NSS_HOST:
return (_nderror);
}
/*
* convert addresses back into sockaddr for gethostbyname.
*/
return (ret);
case NSS_SERV:
/*
* 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".
*/
res);
_nderror =
}
return (_nderror);
}
/*
* Third-parties should optimize their nametoaddr
* libraries for the HOST_SELF case.
*/
return (_nderror);
}
/*
* convert addresses back into servent for getservbyname.
*/
/* LINTED pointer cast */
return (_nderror);
default:
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
{
if (nconf == 0) {
return (_nderror);
}
/*
* 1. gethostbyaddr()/netdir_getbyaddr() special cases:
*/
case NSS_HOST:
/*
* Worth the performance gain: assuming a lot of inet apps
* actively use "127.0.0.1".
*/
/* LINTED pointer cast */
htonl(INADDR_LOOPBACK)) {
(void) mutex_lock(&nd_addr_lock);
(void) mutex_unlock(&nd_addr_lock);
return (_nderror);
}
break;
case NETDIR_BY:
case NETDIR_BY_NOSRV:
{
struct sockaddr_in *sin;
return (_nderror);
}
/*
* Validate the address which was passed
* as the request.
*/
/* LINTED pointer cast */
sizeof (struct sockaddr_in)) ||
return (_nderror);
}
}
break;
case NETDIR_BY6:
case NETDIR_BY_NOSRV6:
{
struct sockaddr_in6 *sin6;
return (_nderror);
}
/*
* Validate the address which was passed
* as the request.
*/
/* LINTED pointer cast */
sizeof (struct sockaddr_in6)) ||
return (_nderror);
}
}
break;
}
/*
* Emphasis on improving performance in the "if" part.
*/
if (nconf->nc_nlookups == 0) {
nss_XbyY_buf_t *ndbuf4host = 0;
nss_XbyY_buf_t *ndbuf4serv = 0;
char *proto =
struct sockaddr_in *sa;
struct sockaddr_in6 *sin6;
int h_errnop;
case NSS_HOST:
if (he == 0)
else
return (_nderror);
case NSS_HOST6:
if (he == 0)
return (ND_NOHOST);
return (ND_OK);
case NSS_SERV:
if (se == 0)
else
return (_nderror);
case NETDIR_BY:
case NETDIR_BY_NOSRV:
if (ndbuf4serv == 0) {
return (_nderror);
}
/* LINTED pointer cast */
/*
* if NETDIR_BY_NOSRV or port == 0 skip the service
* lookup.
*/
ndbuf4serv->buflen);
if (!se) {
/*
* 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.
*/
}
}
if (ndbuf4host == 0) {
if (ndbuf4serv)
return (_nderror);
}
/*
* Since we're going to search the ipnodes (v6) path first,
* we need to treat the address as a v4mapped address.
*/
if (!he) {
/* Failover case, try hosts db for v4 address */
he = DOOR_GETHOSTBYADDR_R((char *)
&h_errnop);
if (!he) {
if (ndbuf4serv)
return (_nderror);
}
/*
* Convert host names and service names into hostserv
* pairs. malloc's will be done, freed using
* netdir_free.
*/
} else {
/*
* Convert host names and service names into hostserv
* pairs. malloc's will be done, freed using
* netdir_free.
*/
}
if (ndbuf4serv)
return (_nderror);
case NETDIR_BY6:
case NETDIR_BY_NOSRV6:
if (ndbuf4serv == 0) {
return (ND_NOMEM);
}
/* LINTED pointer cast */
/*
* if NETDIR_BY_NOSRV6 or port == 0 skip the service
* lookup.
*/
ndbuf4serv->buflen);
if (!se) {
/*
* 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.
*/
}
}
if (ndbuf4host == 0) {
if (ndbuf4serv)
return (_nderror);
}
if (!he) {
if (ndbuf4serv)
return (_nderror);
}
/*
* Convert host names and service names into hostserv
* pairs. malloc's will be done, freed using netdir_free.
*/
if (ndbuf4serv)
return (_nderror);
default:
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.
*/
struct nd_hostservlist *addrs;
struct sockaddr_in sa;
case NSS_HOST:
/* LINTED pointer cast */
/* Hopefully, third-parties get this optimization */
return (_nderror);
}
/*
* convert the host-serv pairs into h_aliases and hent.
*/
return (_nderror);
case NSS_SERV:
/*
* 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".
*/
res);
_nderror =
}
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.
*/
return (_nderror);
}
/*
* convert the host-serv pairs into s_aliases and servent.
*/
return (_nderror);
default:
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
*/
int
{
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);
}
_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
* their signatures here. Just in case.
*/
struct hostent *
{
if (res != NSS_SUCCESS)
}
struct hostent *
{
if (res != NSS_SUCCESS)
}
struct hostent *
{
if (res != NSS_SUCCESS)
}
struct hostent *
{
if (res != NSS_SUCCESS)
}
static void
{
p->name = NSS_DBNAM_SERVICES;
}
struct servent *
{
}
struct servent *
{
}
/*
* 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
{
char numbuf[12];
char *numend;
return (NSS_STR_PARSE_PARSE);
}
p = instr;
p++;
}
namestart = p;
p++; /* Skip over the canonical name */
}
return (NSS_STR_PARSE_ERANGE);
}
p++;
}
fieldstart = p;
do {
return (NSS_STR_PARSE_PARSE);
}
} while (*p++ != '/');
/* Syntax error -- supposed number is empty or too long */
return (NSS_STR_PARSE_PARSE);
}
if (*numend != '\0') {
/* Syntax error -- port number isn't a number */
return (NSS_STR_PARSE_PARSE);
}
fieldstart = p;
p++; /* Scan the protocol name */
}
return (NSS_STR_PARSE_ERANGE);
}
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.
*/
char **ptr;
sizeof (char *));
/* hope they don't try to peek in */
return (NSS_STR_PARSE_ERANGE);
} else {
*ptr = 0;
return (NSS_STR_PARSE_SUCCESS);
}
}
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 __ifaddr {
union __v4v6addr addr;
union __v4v6addr mask;
};
struct ifinfo {
int count;
};
#define ADDR_NUMCLASSES 2
void *);
int __inet_address_is_local_af(void *, sa_family_t, void *);
/*
* The number of nanoseconds the order_haddrlist_inet() function waits
* to retreive IP interface information. The default is five minutes.
*/
/*
* 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
* the order of addresses returned by the nameserver needs to be
* maintained. (DNS round robin feature is one example)
*/
void
{
char **addrptr;
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) {
nosort = _read_nsw_file();
}
(void) mutex_unlock(&checksortcfg_lock);
if (nosort)
return;
/* Count the addresses to sort */
addrcount = 0;
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:
break;
case AF_INET6:
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
{
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
*/
(sizeof (struct in_addr *) + sizeof (addr_class_t));
return;
/* LINTED pointer cast */
/* LINTED pointer cast */
/*
* Get a read lock, and check if the interface information
* is too old.
*/
(void) rw_rdlock(&localinfo_lock);
/* 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.
*/
(void) rw_unlock(&localinfo_lock);
return;
}
}
/* 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.
*/
(void) rw_unlock(&localinfo_lock);
return;
}
}
/*
* Classify the addresses. We also maintain the classcount
* array to keep track of the number of addresses in each
* class.
*/
for (i = 0; i < addrcount; i++) {
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++) {
rc += classcount[i];
}
/* Now for the actual rearrangement of the addresses */
for (i = 0; i < addrcount; i++) {
}
/* Copy the sorted list to inaddrlist */
}
/*
* This function implements the IPv6 Default Address Selection's
* destination address ordering mechanism. The algorithm is described
* in getaddrinfo(3SOCKET).
*/
static void
{
return;
/* Initialize the dstinfo array we'll use for SIOCGDSTINFO */
dinfoptr++;
}
addrcount * sizeof (struct dstinforeq)) < 0) {
return;
}
/* Sort the dinfo array */
/* Copy the addresses back into in6addrlist */
dinfoptr++;
}
}
/*
* Determine number of leading bits that are common between two addresses.
* Only consider bits which fall within the prefix length plen.
*/
static uint_t
{
uint_t i;
for (i = 0; i < 4; 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.
*/
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.
*/
/*
* 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
{
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
{
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
{
return (RULE_PREFER_DB);
return (RULE_PREFER_DA);
return (RULE_EQUAL);
}
/* Prefer the address whose source address isn't deprecated. */
static int
{
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
{
return (RULE_EQUAL);
if (da->dir_labelmatch)
return (RULE_PREFER_DA);
return (RULE_PREFER_DB);
}
/* Prefer the address with the higher precedence. */
static int
{
return (RULE_EQUAL);
return (RULE_PREFER_DA);
return (RULE_PREFER_DB);
}
/* Prefer the address whose output interface isn't an IP tunnel */
static int
{
/* Get the common case out of the way early */
return (RULE_EQUAL);
return (RULE_EQUAL);
if (isbtun)
return (RULE_PREFER_DA);
return (RULE_PREFER_DB);
}
/* Prefer the address with the smaller scope. */
static int
{
return (RULE_EQUAL);
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
{
/*
* At this point, the order doesn't matter if the two addresses
* aren't of the same address family.
*/
return (RULE_EQUAL);
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
{
};
result = 0;
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
{
struct nd_addrlist *result;
int num;
/* Address count */
num = 0;
/*
* Exclude IPv4-mapped IPv6 addresses from the count, as
* these are not included in the nd_addrlist we return.
*/
if (!IN6_IS_ADDR_V4MAPPED(*in6addr))
num++;
} else {
num++;
}
if (num == 0)
return (ND_NOHOST);
if (result == 0)
return (ND_NOMEM);
return (ND_NOMEM);
}
return (ND_NOMEM);
}
na++;
sin++;
}
return (ND_NOMEM);
}
if (IN6_IS_ADDR_V4MAPPED(*in6addr))
continue;
na++;
sin6++;
}
}
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
{
struct nd_hostservlist *result;
struct nd_hostserv *hs;
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.
*/
if (se) {
};
} else
servs = 1;
return (ND_NOMEM);
}
for (j = 0; j < servs; j++) {
if (i == 0)
else
if (j == 0) {
if (se)
else {
/* Convert to a number string */
char stmp[16];
}
} else
return (ND_NOMEM);
}
hs++;
}
if (i)
hn++;
}
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
{
int i, count;
char **addrvec;
sizeof (*addr6p);
/*
* Build addrlist at start of buffer (after name); store the
* addresses themselves at the end of the buffer.
*/
sizeof (*addrp));
return (ND_NOMEM);
--addrp;
/* LINTED pointer cast */
sizeof (*addrp));
}
} else {
sizeof (*addr6p));
return (ND_NOMEM);
--addr6p;
/* LINTED pointer cast */
sizeof (*addr6p));
}
}
*addrvec = 0;
return (ND_OK);
}
/*
* Process results from nd_addrlist ( returned by netdir_getbyname)
* into a servent using buf.
*/
int
{
size_t i;
return (ND_NOMEM);
buffer += i;
return (ND_NOMEM);
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
{
int i, count;
char *aliasp;
char **aliasvec;
struct sockaddr_in *sa;
struct nd_hostserv *hs;
const char *la;
/* First, give the lonely address a specious home in h_addr_list. */
/* LINTED pointer cast */
sizeof (*aliasvec));
*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).
*/
if (!hs)
return (ND_NOHOST);
return (ND_NOMEM);
/*
* 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.
*/
return (ND_NOMEM);
}
*aliasvec = 0;
return (ND_OK);
}
/*
* Process results from nd_hostservlist ( returned by netdir_getbyaddr)
* into a servent using buf.
*/
int
{
int i, count;
char *aliasp;
char **aliasvec;
struct nd_hostserv *hs;
const char *host_cname;
/*
* Build s_aliases at start of buffer;
* store proto and aliases at the end of the buffer (before h_name).
*/
if (!hs)
return (ND_NOHOST);
return (ND_NOMEM);
/*
* Assumption: the netdir nametoaddr_libs
* do a host aliases first and serv aliases next
* enumeration for creating the list of hostserv
* structures.
*/
for (i = 0;
i++, hs++) {
return (ND_NOMEM);
}
return (ND_OK);
}
static int
{
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
{
int fd;
char *devpath;
int retv;
switch (af) {
case AF_INET6:
break;
case AF_INET:
case AF_UNSPEC:
default:
}
return (-1);
}
break;
}
return (retv);
}
static int
{
}
static struct ifinfo *
get_local_info(void)
{
int numifs;
int n;
lifn.lifn_flags = 0;
} else {
}
/*
* Add a small fudge factor in case interfaces get plumbed between
* the call to SIOCGLIFNUM and SIOCGLIFCONF.
*/
else
return (NULL);
}
lifc.lifc_flags = 0;
/*
* 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.
*/
goto getifnum;
"ioctl (get interface configuration): %m");
return (NULL);
}
/* LINTED pointer cast */
return (NULL);
}
/* LINTED pointer cast */
int af;
/* Squirrel away the address */
continue;
"n2a get_local_info: "
"ioctl (get interface flags): %m");
continue;
}
continue;
"n2a get_local_info: "
"ioctl (get interface netmask): %m");
continue;
}
continue;
}
return (localinfo);
}
static int
void *addr) {
switch (type) {
case IF_ADDR:
} else {
}
break;
case IF_MASK:
} else {
}
} 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 *
{
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
int i, a;
if (localinfo == 0)
return (0);
}
break;
}
return (1);
} else {
return (1);
}
}
}
return (0);
}
int
{
}
int
{
int ret;
if (taddr == 0)
return (0);
/* LINTED pointer cast */
/* LINTED pointer cast */
/* LINTED pointer cast */
return (ret);
}
int
__inet_address_count(void *p)
{
if (lp != 0) {
} else {
return (0);
}
}
__inet_get_addr(void *p, int n)
{
return (0);
}
__inet_get_network(void *p, int n)
{
return (0);
}
char *
{
char *uaddr;
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
return (0);
return (0);
} else {
return (0);
}
return (uaddr);
}
char *
__inet_get_networka(void *p, int n)
{
return (0);
char buf[INET6_ADDRSTRLEN];
int i;
}
} else {
}
}
static int
{
int i;
for (i = 0; i < n; i++) {
return (1);
}
return (0);
}
static int
{
struct sockaddr_in *sin;
struct in_addr a;
int fd;
int n, i, numifs;
char *buf;
int use_loopback = 0;
if (fd < 0) {
"broadcast: open to get interface configuration: %m");
return (0);
}
return (0);
}
return (0);
}
/*
* Ideally, this ioctl should also tell me, how many bytes were
* finally allocated, but it doesnt.
*/
"broadcast: ioctl (get interface configuration): %m");
return (0);
}
/* LINTED pointer cast */
n > 0; n--, ifr++) {
"ioctl (get interface flags): %m");
continue;
}
continue;
/* LINTED pointer cast */
/* May not work with other implementation */
a = _inet_makeaddr(
(*addrs)[i++] = a;
} else {
/* LINTED pointer cast */
a = ((struct sockaddr_in *)
(*addrs)[i++] = a;
}
continue;
}
/* LINTED pointer cast */
(*addrs)[i++] = a;
continue;
}
continue;
/* LINTED pointer cast */
a = ((struct sockaddr_in *)
(*addrs)[i++] = a;
continue;
}
}
if (i == 0 && !use_loopback) {
use_loopback = 1;
goto retry;
}
if (i)
else
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
{
if (net < 128)
else if (net < 65536)
else if (net < 16777216L)
else
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)
{
do {
return (B_FALSE);
sizeof (DONT_SORT2) - 1) == 0)) {
break;
}
}
return (nosort);
}