#include "mt.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/byteorder.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <thread.h>
#include <synch.h>
#include <sys/utsname.h>
#include <netdb.h>
#include <netconfig.h>
#include <netdir.h>
#include <tiuser.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <inet/ip.h>
#include <inet/ip6_asp.h>
#include <sys/dlpi.h>
#include <nss_dbdefs.h>
#include <nss_netdir.h>
#include <syslog.h>
#include <nsswitch.h>
#include "nss.h"
#define MAXIFS 32
#define UDPDEV "/dev/udp"
#define UDP6DEV "/dev/udp6"
#define DOOR_GETHOSTBYNAME_R _switch_gethostbyname_r
#define DOOR_GETHOSTBYADDR_R _switch_gethostbyaddr_r
#define DOOR_GETIPNODEBYNAME_R _switch_getipnodebyname_r
#define DOOR_GETIPNODEBYADDR_R _switch_getipnodebyaddr_r
#define DONT_SORT "SORT_ADDRS=NO"
#define DONT_SORT2 "SORT_ADDRS=FALSE"
#define LINESIZE 100
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};
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};
static mutex_t nd6_addr_lock = DEFAULTMUTEX;
static struct sockaddr_in6 sa6_con;
static struct netbuf nd6_conbuf = {sizeof (sa6_con),\
sizeof (sa6_con), (char *)&sa6_con};
static struct nd_addrlist nd6_conaddrlist = {1, &nd6_conbuf};
#define LOCALHOST "localhost"
struct servent *_switch_getservbyname_r(const char *, const char *,
struct servent *, char *, int);
struct servent *_switch_getservbyport_r(int, const char *, struct servent *,
char *, int);
static int __herrno2netdir(int h_errnop);
static struct ifinfo *get_local_info(void);
static int getbroadcastnets(struct netconfig *, struct in_addr **);
static int hent2ndaddr(int, char **, int *, struct nd_addrlist **);
static int ndaddr2hent(int, const char *, struct nd_addrlist *,
struct hostent *, char *, int);
static int hsents2ndhostservs(struct hostent *, struct servent *, ushort_t,
struct nd_hostservlist **);
static int ndaddr2srent(const char *, const char *, ushort_t, struct servent *,
char *, int);
static int ndhostserv2hent(struct netbuf *, struct nd_hostservlist *,
struct hostent *, char *, int);
static int ndhostserv2srent(int, const char *, struct nd_hostservlist *,
struct servent *, char *, int);
static int nd2herrno(int nerr);
static void order_haddrlist_inet(char **haddrlist, size_t addrcount);
static void order_haddrlist_inet6(char **haddrlist, size_t addrcount);
static int dstcmp(const void *, const void *);
static int nss_strioctl(int af, int cmd, void *ptr, int ilen);
static struct in_addr _inet_makeaddr(in_addr_t, in_addr_t);
static boolean_t _read_nsw_file(void);
static int
inetdir_free(int ret, struct in_addr *inaddrs, char **baddrlist)
{
if (inaddrs)
free(inaddrs);
if (baddrlist)
free(baddrlist);
_nderror = ret;
return (ret);
}
int
_get_hostserv_inetnetdir_byname(struct netconfig *nconf,
struct nss_netdirbyname_in *args, union nss_netdirbyname_out *res)
{
int server_port;
int *servp = &server_port;
char **haddrlist;
uint32_t dotnameaddr;
char *dotnamelist[2];
struct in_addr *inaddrs = NULL;
struct in6_addr v6nameaddr;
char **baddrlist = NULL;
if (nconf == NULL) {
_nderror = ND_BADARG;
return (ND_BADARG);
}
switch (args->op_t) {
case NSS_HOST:
if (strcmp(args->arg.nss.host.name, LOCALHOST) == 0) {
(void) mutex_lock(&nd_addr_lock);
IN_SET_LOOPBACK_ADDR(&sa_con);
_nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
&nd_conaddrlist, res->nss.host.hent,
args->arg.nss.host.buf,
args->arg.nss.host.buflen);
(void) mutex_unlock(&nd_addr_lock);
if (_nderror != ND_OK)
*(res->nss.host.herrno_p) =
nd2herrno(_nderror);
return (_nderror);
}
if (inet_aton(args->arg.nss.host.name,
(struct in_addr *)&dotnameaddr)) {
(void) mutex_lock(&nd_addr_lock);
(void) memset(&sa_con, 0, sizeof (sa_con));
sa_con.sin_family = AF_INET;
sa_con.sin_addr.s_addr = dotnameaddr;
_nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
&nd_conaddrlist, res->nss.host.hent,
args->arg.nss.host.buf,
args->arg.nss.host.buflen);
(void) mutex_unlock(&nd_addr_lock);
if (_nderror != ND_OK)
*(res->nss.host.herrno_p) =
nd2herrno(_nderror);
return (_nderror);
}
break;
case NSS_HOST6:
if (strchr(args->arg.nss.host6.name, ':') != NULL &&
(inet_pton(AF_INET6, args->arg.nss.host6.name,
&v6nameaddr) != 0)) {
int ret;
(void) mutex_lock(&nd6_addr_lock);
(void) memset(&sa6_con, 0, sizeof (sa6_con));
sa6_con.sin6_family = AF_INET6;
(void) memcpy(&(sa6_con.sin6_addr.s6_addr),
&v6nameaddr, sizeof (struct in6_addr));
ret = ndaddr2hent(AF_INET6,
args->arg.nss.host6.name,
&nd6_conaddrlist, res->nss.host.hent,
args->arg.nss.host6.buf,
args->arg.nss.host6.buflen);
(void) mutex_unlock(&nd6_addr_lock);
if (ret != ND_OK)
*(res->nss.host.herrno_p) = nd2herrno(ret);
else
res->nss.host.hent->h_aliases = NULL;
return (ret);
}
break;
case NETDIR_BY:
if (args->arg.nd_hs == 0) {
_nderror = ND_BADARG;
return (ND_BADARG);
}
if (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 {
servp = NULL;
}
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) {
dotnamelist[0] = (char *)&dotnameaddr;
dotnamelist[1] = NULL;
haddrlist = dotnamelist;
} else if ((strcmp(args->arg.nd_hs->h_host,
HOST_BROADCAST) == 0)) {
int i, bnets;
bnets = getbroadcastnets(nconf, &inaddrs);
if (bnets == 0) {
_nderror = ND_NOHOST;
return (ND_NOHOST);
}
baddrlist = malloc((bnets+1)*sizeof (char *));
if (baddrlist == NULL)
return (inetdir_free(ND_NOMEM, inaddrs,
baddrlist));
for (i = 0; i < bnets; i++)
baddrlist[i] = (char *)&inaddrs[i];
baddrlist[i] = NULL;
haddrlist = baddrlist;
} else {
haddrlist = 0;
}
if (haddrlist && servp) {
int ret;
ret = hent2ndaddr(AF_INET, haddrlist, servp,
res->nd_alist);
return (inetdir_free(ret, inaddrs, baddrlist));
}
break;
case NETDIR_BY6:
if (args->arg.nd_hs == 0) {
_nderror = ND_BADARG;
return (ND_BADARG);
}
if (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 {
servp = NULL;
}
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 ((inet_pton(AF_INET6,
args->arg.nd_hs->h_host,
&v6nameaddr)) != 0) {
dotnamelist[0] = (char *)&v6nameaddr;
dotnamelist[1] = NULL;
haddrlist = dotnamelist;
}
else
return (ND_NOHOST);
} else if ((strcmp(args->arg.nd_hs->h_host,
HOST_BROADCAST) == 0)) {
return (ND_NOHOST);
} else {
haddrlist = 0;
}
if (haddrlist && servp) {
int ret;
ret = hent2ndaddr(AF_INET6, haddrlist,
servp, res->nd_alist);
return (inetdir_free(ret, inaddrs, baddrlist));
}
break;
}
if (nconf->nc_nlookups == 0) {
struct hostent *he = NULL, *tmphe;
struct servent *se;
int ret;
nss_XbyY_buf_t *ndbuf4switch = 0;
switch (args->op_t) {
case NSS_HOST:
he = DOOR_GETHOSTBYNAME_R(args->arg.nss.host.name,
res->nss.host.hent, args->arg.nss.host.buf,
args->arg.nss.host.buflen,
res->nss.host.herrno_p);
if (he == NULL)
return (_nderror = ND_NOHOST);
return (_nderror = ND_OK);
case NSS_HOST6:
he = DOOR_GETIPNODEBYNAME_R(args->arg.nss.host6.name,
res->nss.host.hent, args->arg.nss.host.buf,
args->arg.nss.host6.buflen,
args->arg.nss.host6.af_family,
args->arg.nss.host6.flags,
res->nss.host.herrno_p);
if (he == NULL)
return (_nderror = ND_NOHOST);
return (_nderror = ND_OK);
case NSS_SERV:
se = _switch_getservbyname_r(args->arg.nss.serv.name,
args->arg.nss.serv.proto,
res->nss.serv, args->arg.nss.serv.buf,
args->arg.nss.serv.buflen);
_nderror = ND_OK;
if (se == 0)
_nderror = ND_NOSERV;
return (_nderror);
case NETDIR_BY:
if (servp == 0) {
char *proto = (strcmp(nconf->nc_proto,
NC_TCP) == 0) ? NC_TCP : NC_UDP;
ndbuf4switch = _nss_XbyY_buf_alloc(
sizeof (struct servent), NSS_BUFLEN_SERVICES);
if (ndbuf4switch == 0)
return (inetdir_free(ND_NOMEM, inaddrs,
baddrlist));
se = _switch_getservbyname_r(args->arg.nd_hs->h_serv,
proto, ndbuf4switch->result,
ndbuf4switch->buffer, ndbuf4switch->buflen);
if (!se) {
NSS_XbyY_FREE(&ndbuf4switch);
return (inetdir_free(ND_NOSERV, inaddrs,
baddrlist));
}
server_port = se->s_port;
NSS_XbyY_FREE(&ndbuf4switch);
}
if (haddrlist == 0) {
int h_errnop = 0;
ndbuf4switch = _nss_XbyY_buf_alloc(
sizeof (struct hostent),
NSS_BUFLEN_HOSTS);
if (ndbuf4switch == 0) {
_nderror = ND_NOMEM;
return (ND_NOMEM);
}
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) {
he = DOOR_GETHOSTBYNAME_R(
args->arg.nd_hs->h_host,
ndbuf4switch->result, ndbuf4switch->buffer,
ndbuf4switch->buflen, &h_errnop);
if (he == NULL) {
NSS_XbyY_FREE(&ndbuf4switch);
_nderror = __herrno2netdir(h_errnop);
return (_nderror);
}
ret = hent2ndaddr(AF_INET, he->h_addr_list,
&server_port, res->nd_alist);
} else {
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;
ret = hent2ndaddr(AF_INET, haddrlist,
&server_port, res->nd_alist);
return (inetdir_free(ret, inaddrs, baddrlist));
}
case NETDIR_BY6:
if (servp == 0) {
char *proto = (strcmp(nconf->nc_proto,
NC_TCP) == 0) ? NC_TCP : NC_UDP;
ndbuf4switch = _nss_XbyY_buf_alloc(
sizeof (struct servent),
NSS_BUFLEN_SERVICES);
if (ndbuf4switch == 0)
return (inetdir_free(ND_NOMEM, inaddrs,
baddrlist));
se = _switch_getservbyname_r(
args->arg.nd_hs->h_serv,
proto, ndbuf4switch->result,
ndbuf4switch->buffer, ndbuf4switch->buflen);
if (!se) {
NSS_XbyY_FREE(&ndbuf4switch);
return (inetdir_free(ND_NOSERV, inaddrs,
baddrlist));
}
server_port = se->s_port;
NSS_XbyY_FREE(&ndbuf4switch);
}
if (haddrlist == 0) {
int h_errnop = 0;
ndbuf4switch = _nss_XbyY_buf_alloc(
sizeof (struct hostent),
NSS_BUFLEN_HOSTS);
if (ndbuf4switch == 0) {
_nderror = ND_NOMEM;
return (ND_NOMEM);
}
he = DOOR_GETIPNODEBYNAME_R(
args->arg.nd_hs->h_host,
ndbuf4switch->result, ndbuf4switch->buffer,
ndbuf4switch->buflen,
args->arg.nss.host6.af_family,
args->arg.nss.host6.flags, &h_errnop);
if (he == NULL) {
NSS_XbyY_FREE(&ndbuf4switch);
_nderror = __herrno2netdir(h_errnop);
return (_nderror);
}
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;
ret = hent2ndaddr(AF_INET6, haddrlist,
&server_port, res->nd_alist);
return (inetdir_free(ret, inaddrs, baddrlist));
}
default:
_nderror = ND_BADARG;
return (ND_BADARG);
}
} else {
if (inaddrs)
free(inaddrs);
if (baddrlist)
free(baddrlist);
}
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);
}
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) {
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);
}
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);
}
_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);
}
}
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);
}
switch (args->op_t) {
case NSS_HOST:
if (*(uint32_t *)(args->arg.nss.host.addr) ==
htonl(INADDR_LOOPBACK)) {
(void) mutex_lock(&nd_addr_lock);
IN_SET_LOOPBACK_ADDR(&sa_con);
_nderror = ndaddr2hent(AF_INET, LOCALHOST,
&nd_conaddrlist, res->nss.host.hent,
args->arg.nss.host.buf,
args->arg.nss.host.buflen);
(void) mutex_unlock(&nd_addr_lock);
if (_nderror != ND_OK)
*(res->nss.host.herrno_p) =
nd2herrno(_nderror);
return (_nderror);
}
break;
case NETDIR_BY:
case NETDIR_BY_NOSRV:
{
struct sockaddr_in *sin;
if (args->arg.nd_nbuf == NULL) {
_nderror = ND_BADARG;
return (_nderror);
}
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);
}
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;
}
if (nconf->nc_nlookups == 0) {
struct hostent *he = NULL, *tmphe;
struct servent *se = NULL;
nss_XbyY_buf_t *ndbuf4host = 0;
nss_XbyY_buf_t *ndbuf4serv = 0;
char *proto =
(strcmp(nconf->nc_proto, NC_TCP) == 0) ? NC_TCP : NC_UDP;
struct sockaddr_in *sa;
struct sockaddr_in6 *sin6;
struct in_addr *addr4 = 0;
struct in6_addr v4mapbuf;
int h_errnop;
switch (args->op_t) {
case NSS_HOST:
he = DOOR_GETHOSTBYADDR_R(args->arg.nss.host.addr,
args->arg.nss.host.len, args->arg.nss.host.type,
res->nss.host.hent, args->arg.nss.host.buf,
args->arg.nss.host.buflen,
res->nss.host.herrno_p);
if (he == 0)
_nderror = ND_NOHOST;
else
_nderror = ND_OK;
return (_nderror);
case NSS_HOST6:
he = DOOR_GETIPNODEBYADDR_R(args->arg.nss.host.addr,
args->arg.nss.host.len, args->arg.nss.host.type,
res->nss.host.hent, args->arg.nss.host.buf,
args->arg.nss.host.buflen,
res->nss.host.herrno_p);
if (he == 0)
return (ND_NOHOST);
return (ND_OK);
case NSS_SERV:
se = _switch_getservbyport_r(args->arg.nss.serv.port,
args->arg.nss.serv.proto,
res->nss.serv, args->arg.nss.serv.buf,
args->arg.nss.serv.buflen);
if (se == 0)
_nderror = ND_NOSERV;
else
_nderror = ND_OK;
return (_nderror);
case NETDIR_BY:
case NETDIR_BY_NOSRV:
ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
NSS_BUFLEN_SERVICES);
if (ndbuf4serv == 0) {
_nderror = ND_NOMEM;
return (_nderror);
}
sa = (struct sockaddr_in *)(args->arg.nd_nbuf->buf);
addr4 = (struct in_addr *)&(sa->sin_addr);
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);
}
}
ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
NSS_BUFLEN_HOSTS);
if (ndbuf4host == 0) {
if (ndbuf4serv)
NSS_XbyY_FREE(&ndbuf4serv);
_nderror = ND_NOMEM;
return (_nderror);
}
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) {
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);
}
h_errnop = hsents2ndhostservs(he, se,
sa->sin_port, res->nd_hslist);
} else {
h_errnop = hsents2ndhostservs(he, se,
sa->sin_port, res->nd_hslist);
freehostent(he);
}
NSS_XbyY_FREE(&ndbuf4host);
if (ndbuf4serv)
NSS_XbyY_FREE(&ndbuf4serv);
_nderror = __herrno2netdir(h_errnop);
return (_nderror);
case NETDIR_BY6:
case NETDIR_BY_NOSRV6:
ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
NSS_BUFLEN_SERVICES);
if (ndbuf4serv == 0) {
_nderror = ND_NOMEM;
return (ND_NOMEM);
}
sin6 = (struct sockaddr_in6 *)(args->arg.nd_nbuf->buf);
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);
}
}
ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
NSS_BUFLEN_HOSTS);
if (ndbuf4host == 0) {
if (ndbuf4serv)
NSS_XbyY_FREE(&ndbuf4serv);
_nderror = ND_NOMEM;
return (_nderror);
}
he = DOOR_GETIPNODEBYADDR_R((char *)&(sin6->sin6_addr),
16, sin6->sin6_family, ndbuf4host->result,
ndbuf4host->buffer,
ndbuf4host->buflen, &h_errnop);
if (!he) {
NSS_XbyY_FREE(&ndbuf4host);
if (ndbuf4serv)
NSS_XbyY_FREE(&ndbuf4serv);
_nderror = __herrno2netdir(h_errnop);
return (_nderror);
}
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);
}
}
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;
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);
}
_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) {
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);
}
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);
}
_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);
}
}
static DEFINE_NSS_DB_ROOT(db_root_hosts);
static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
static DEFINE_NSS_DB_ROOT(db_root_services);
int
__nss2herrno(nss_status_t nsstat)
{
switch (nsstat) {
case NSS_SUCCESS:
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);
}
return (NO_RECOVERY);
}
nss_status_t
_herrno2nss(int h_errno)
{
switch (h_errno) {
case 0:
return (NSS_SUCCESS);
case TRY_AGAIN:
return (NSS_TRYAGAIN);
case NO_RECOVERY:
case NETDB_INTERNAL:
return (NSS_UNAVAIL);
case HOST_NOT_FOUND:
case NO_DATA:
default:
return (NSS_NOTFOUND);
}
}
static int
__herrno2netdir(int h_errnop)
{
switch (h_errnop) {
case 0:
return (ND_OK);
case HOST_NOT_FOUND:
return (ND_NOHOST);
case TRY_AGAIN:
return (ND_TRY_AGAIN);
case NO_RECOVERY:
case NETDB_INTERNAL:
return (ND_NO_RECOVERY);
case NO_DATA:
return (ND_NO_DATA);
default:
return (ND_NOHOST);
}
}
struct hostent *
_switch_gethostbyname_r(const char *name, struct hostent *result, char *buffer,
int buflen, int *h_errnop)
{
nss_XbyY_args_t arg;
nss_status_t res;
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
arg.key.name = name;
arg.stayopen = 0;
res = nss_search(&db_root_hosts, _nss_initf_hosts,
NSS_DBOP_HOSTS_BYNAME, &arg);
arg.status = res;
if (res != NSS_SUCCESS)
*h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
if (arg.returnval != NULL)
order_haddrlist_af(result->h_addrtype, result->h_addr_list);
return ((struct hostent *)NSS_XbyY_FINI(&arg));
}
struct hostent *
_switch_getipnodebyname_r(const char *name, struct hostent *result,
char *buffer, int buflen, int af_family, int flags, int *h_errnop)
{
nss_XbyY_args_t arg;
nss_status_t res;
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
arg.key.ipnode.name = name;
arg.key.ipnode.af_family = af_family;
arg.key.ipnode.flags = flags;
arg.stayopen = 0;
res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
NSS_DBOP_IPNODES_BYNAME, &arg);
arg.status = res;
if (res != NSS_SUCCESS)
*h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
if (arg.returnval != NULL)
order_haddrlist_af(result->h_addrtype, result->h_addr_list);
return ((struct hostent *)NSS_XbyY_FINI(&arg));
}
struct hostent *
_switch_gethostbyaddr_r(const char *addr, int len, int type,
struct hostent *result, char *buffer, int buflen, int *h_errnop)
{
nss_XbyY_args_t arg;
nss_status_t res;
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
arg.key.hostaddr.addr = addr;
arg.key.hostaddr.len = len;
arg.key.hostaddr.type = type;
arg.stayopen = 0;
res = nss_search(&db_root_hosts, _nss_initf_hosts,
NSS_DBOP_HOSTS_BYADDR, &arg);
arg.status = res;
if (res != NSS_SUCCESS)
*h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
return (struct hostent *)NSS_XbyY_FINI(&arg);
}
struct hostent *
_switch_getipnodebyaddr_r(const char *addr, int len, int type,
struct hostent *result, char *buffer, int buflen, int *h_errnop)
{
nss_XbyY_args_t arg;
nss_status_t res;
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
arg.key.hostaddr.addr = addr;
arg.key.hostaddr.len = len;
arg.key.hostaddr.type = type;
arg.stayopen = 0;
res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
NSS_DBOP_IPNODES_BYADDR, &arg);
arg.status = res;
if (res != NSS_SUCCESS)
*h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
return (struct hostent *)NSS_XbyY_FINI(&arg);
}
static void
_nss_initf_services(nss_db_params_t *p)
{
p->name = NSS_DBNAM_SERVICES;
p->default_config = NSS_DEFCONF_SERVICES;
}
struct servent *
_switch_getservbyname_r(const char *name, const char *proto,
struct servent *result, char *buffer, int buflen)
{
nss_XbyY_args_t arg;
nss_status_t res;
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
arg.key.serv.serv.name = name;
arg.key.serv.proto = proto;
arg.stayopen = 0;
res = nss_search(&db_root_services, _nss_initf_services,
NSS_DBOP_SERVICES_BYNAME, &arg);
arg.status = res;
return ((struct servent *)NSS_XbyY_FINI(&arg));
}
struct servent *
_switch_getservbyport_r(int port, const char *proto, struct servent *result,
char *buffer, int buflen)
{
nss_XbyY_args_t arg;
nss_status_t res;
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
arg.key.serv.serv.port = port;
arg.key.serv.proto = proto;
arg.stayopen = 0;
res = nss_search(&db_root_services, _nss_initf_services,
NSS_DBOP_SERVICES_BYPORT, &arg);
arg.status = res;
return ((struct servent *)NSS_XbyY_FINI(&arg));
}
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++;
}
namelen = p - namestart;
if (buflen <= namelen) {
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)) {
return (NSS_STR_PARSE_PARSE);
}
} while (*p++ != '/');
fieldlen = p - fieldstart - 1;
if (fieldlen == 0 || fieldlen >= sizeof (numbuf)) {
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') {
return (NSS_STR_PARSE_PARSE);
}
fieldstart = p;
while (p < limit && !isspace(*p)) {
p++;
}
fieldlen = p - fieldstart + 1;
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++;
}
if (p >= limit || *p == '#') {
char **ptr;
ptr = (char **)ROUND_UP(buffer + namelen + 1 + fieldlen,
sizeof (char *));
if ((char *)ptr >= buffer + buflen) {
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);
}
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))
#define IFINFOTIMEOUT ((hrtime_t)300 * NANOSEC)
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;
(void) mutex_lock(&checksortcfg_lock);
if (checksortcfg == B_TRUE) {
checksortcfg = B_FALSE;
nosort = _read_nsw_file();
}
(void) mutex_unlock(&checksortcfg_lock);
if (nosort)
return;
addrcount = 0;
for (addrptr = haddrlist; *addrptr != NULL; addrptr++)
addrcount++;
if (addrcount <= 1)
return;
switch (af) {
case AF_INET:
order_haddrlist_inet(haddrlist, addrcount);
break;
case AF_INET6:
order_haddrlist_inet6(haddrlist, addrcount);
break;
default:
break;
}
}
static void
order_haddrlist_inet(char **haddrlist, size_t addrcount)
{
static struct ifinfo *localinfo = NULL;
static hrtime_t then = 0;
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;
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 *)));
(void) rw_rdlock(&localinfo_lock);
now = gethrtime();
if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
(void) rw_unlock(&localinfo_lock);
(void) rw_wrlock(&localinfo_lock);
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;
}
(void) rw_unlock(&localinfo_lock);
(void) rw_rdlock(&localinfo_lock);
if (localinfo == NULL) {
(void) rw_unlock(&localinfo_lock);
free(sortbuf);
return;
}
}
(void) memset(classcount, 0, sizeof (classcount));
for (i = 0; i < addrcount; i++) {
if (__inet_address_is_local_af(localinfo, AF_INET,
inaddrlist[i]))
sortclass[i] = ADDR_ONLINK;
else
sortclass[i] = ADDR_OFFLINK;
classcount[sortclass[i]]++;
}
(void) rw_unlock(&localinfo_lock);
for (rc = 0, i = 0; i < ADDR_NUMCLASSES; i++) {
classnext[i] = &sorted[rc];
rc += classcount[i];
}
for (i = 0; i < addrcount; i++) {
*(classnext[sortclass[i]]) = inaddrlist[i];
classnext[sortclass[i]]++;
}
(void) memcpy(inaddrlist, sorted,
addrcount * sizeof (struct in_addr *));
free(sortbuf);
}
static void
order_haddrlist_inet6(char **haddrlist, size_t addrcount)
{
struct dstinforeq *dinfo, *dinfoptr;
struct in6_addr **in6addrlist = (struct in6_addr **)haddrlist;
struct in6_addr **in6addr;
if ((dinfo = calloc(addrcount, sizeof (struct dstinforeq))) == NULL)
return;
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;
}
qsort(dinfo, addrcount, sizeof (struct dstinforeq), dstcmp);
dinfoptr = dinfo;
for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
**in6addr = dinfoptr->dir_daddr;
dinfoptr++;
}
free(dinfo);
}
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;
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);
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++;
return (bits);
}
typedef int (*rulef_t)(const struct dstinforeq *, const struct dstinforeq *);
#define RULE_PREFER_DA -1
#define RULE_PREFER_DB 1
#define RULE_EQUAL 0
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
static int
rule_native(const struct dstinforeq *da, const struct dstinforeq *db)
{
boolean_t isatun, isbtun;
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);
}
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);
}
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);
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);
}
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);
}
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;
num = 0;
if (af == AF_INET6) {
in6addrlist = (struct in6_addr **)haddrlist;
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);
}
int
hsents2ndhostservs(struct hostent *he, struct servent *se,
ushort_t port, struct nd_hostservlist **hslist)
{
struct nd_hostservlist *result;
struct nd_hostserv *hs;
int hosts, servs, i, j;
char **hn, **sn;
if ((result = malloc(sizeof (struct nd_hostservlist))) == 0)
return (ND_NOMEM);
for (hn = he->h_aliases, hosts = 1; hn && *hn; hn++, hosts++) {};
if (se) {
for (sn = se->s_aliases, servs = 1; sn && *sn; sn++, servs++) {
};
} else
servs = 1;
if ((hs = calloc(hosts * servs, sizeof (struct nd_hostserv))) == 0) {
free(result);
return (ND_NOMEM);
}
result->h_cnt = servs * hosts;
result->h_hostservs = hs;
for (i = 0, hn = he->h_aliases; i < hosts; i++) {
sn = se ? se->s_aliases : NULL;
for (j = 0; j < servs; j++) {
if (i == 0)
hs->h_host = strdup(he->h_name);
else
hs->h_host = strdup(*hn);
if (j == 0) {
if (se)
hs->h_serv = strdup(se->s_name);
else {
char stmp[16];
(void) sprintf(stmp, "%d", port);
hs->h_serv = strdup(stmp);
}
} else
hs->h_serv = strdup(*sn++);
if ((hs->h_host == 0) || (hs->h_serv == 0)) {
free(result->h_hostservs);
free(result);
return (ND_NOMEM);
}
hs++;
}
if (i)
hn++;
}
*(hslist) = result;
return (ND_OK);
}
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);
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);
}
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);
}
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;
aliasp = (char *)ROUND_UP(buffer, sizeof (sa->sin_addr));
sa = (struct sockaddr_in *)nbuf->buf;
(void) memcpy(aliasp, &(sa->sin_addr), sizeof (sa->sin_addr));
aliasvec = (char **)ROUND_UP(aliasp + sizeof (sa->sin_addr),
sizeof (*aliasvec));
result->h_addr_list = aliasvec;
*aliasvec++ = aliasp;
*aliasvec++ = 0;
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);
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);
}
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;
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;
count = addrs->h_cnt;
for (i = 0;
i < count && hs->h_serv && strcmp(hs->h_host, host_cname) == 0;
i++, hs++) {
size_t len = strlen(hs->h_serv) + 1;
aliasp -= len;
if ((char *)(&aliasvec[2]) > aliasp)
return (ND_NOMEM);
(void) memcpy(aliasp, hs->h_serv, len);
*aliasvec++ = aliasp;
}
*aliasvec = NULL;
return (ND_OK);
}
static int
nd2herrno(int nerr)
{
switch (nerr) {
case ND_OK:
return (0);
case ND_TRY_AGAIN:
return (TRY_AGAIN);
case ND_NO_RECOVERY:
case ND_BADARG:
case ND_NOMEM:
return (NO_RECOVERY);
case ND_NO_DATA:
return (NO_DATA);
case ND_NOHOST:
case ND_NOSERV:
return (HOST_NOT_FOUND);
default:
return (NO_RECOVERY);
}
}
int
nss_ioctl(int af, int cmd, void *arg)
{
int fd;
char *devpath;
int retv;
switch (af) {
case AF_INET6:
devpath = UDP6DEV;
break;
case AF_INET:
case AF_UNSPEC:
default:
devpath = UDPDEV;
}
if ((fd = open(devpath, O_RDONLY)) < 0) {
return (-1);
}
while ((retv = ioctl(fd, cmd, arg)) == -1) {
if (errno != EINTR)
break;
}
(void) close(fd);
return (retv);
}
static int
nss_strioctl(int af, int cmd, void *ptr, int ilen)
{
struct strioctl str;
str.ic_cmd = cmd;
str.ic_timout = 0;
str.ic_len = ilen;
str.ic_dp = ptr;
return (nss_ioctl(af, I_STR, &str));
}
static struct ifinfo *
get_local_info(void)
{
int numifs;
int n;
char *buf = NULL;
size_t needed;
struct lifconf lifc;
struct lifreq lifreq, *lifr;
struct lifnum lifn;
struct ifinfo *localinfo;
lifn.lifn_family = AF_UNSPEC;
lifn.lifn_flags = 0;
getifnum:
if (nss_ioctl(AF_UNSPEC, SIOCGLIFNUM, &lifn) == -1) {
numifs = MAXIFS;
} else {
numifs = lifn.lifn_count;
}
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) {
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 = 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;
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);
}
void *
__inet_get_local_interfaces(void)
{
return (get_local_info());
}
void
__inet_free_local_interfaces(void *p)
{
free(p);
}
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 netbuf nb;
if (localinfo == 0 || nc == 0 || n >= localinfo->count)
return (0);
if (ifaf(n) == AF_INET6) {
if (strcmp(NC_INET6, nc->nc_protofmly) != 0)
return (0);
(void) memset(&sin6, 0, sizeof (sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = ifaddr6(n);
nb.buf = (char *)&sin6;
nb.len = sizeof (sin6);
} else {
if (strcmp(NC_INET, nc->nc_protofmly) != 0)
return (0);
(void) memset(&sin4, 0, sizeof (sin4));
sin4.sin_family = AF_INET;
sin4.sin_addr = ifaddr4(n);
nb.buf = (char *)&sin4;
nb.len = sizeof (sin4);
}
nb.maxlen = nb.len;
uaddr = taddr2uaddr(nc, &nb);
return (uaddr);
}
char *
__inet_get_networka(void *p, int n)
{
struct ifinfo *localinfo = (struct ifinfo *)p;
if (localinfo == 0 || n >= localinfo->count)
return (0);
if (ifaf(n) == AF_INET6) {
char buf[INET6_ADDRSTRLEN];
struct in6_addr in6;
int i;
for (i = 0; i < sizeof (in6.s6_addr); i++) {
in6.s6_addr[i] = ifaddr6(n).s6_addr[i] &
ifmask6(n).s6_addr[i];
}
return (strdup(inet_ntop(AF_INET6, &in6, buf, sizeof (buf))));
} else {
struct in_addr in4;
in4.s_addr = ifaddr4(n).s_addr & ifmask4(n).s_addr;
return (strdup(inet_ntoa(in4)));
}
}
static int
in_list(struct in_addr *addrs, int n, struct in_addr a)
{
int i;
for (i = 0; i < n; i++) {
if (addrs[i].s_addr == a.s_addr)
return (1);
}
return (0);
}
static int
getbroadcastnets(struct netconfig *tp, struct in_addr **addrs)
{
struct ifconf ifc;
struct ifreq ifreq, *ifr;
struct sockaddr_in *sin;
struct in_addr a;
int fd;
int n, i, numifs;
char *buf;
int use_loopback = 0;
_nderror = ND_SYSTEM;
fd = open(tp->nc_device, O_RDONLY);
if (fd < 0) {
(void) syslog(LOG_ERR,
"broadcast: open to get interface configuration: %m");
return (0);
}
if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0)
numifs = MAXIFS;
buf = malloc(numifs * sizeof (struct ifreq));
if (buf == NULL) {
(void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
(void) close(fd);
return (0);
}
*addrs = malloc(numifs * sizeof (struct in_addr));
if (*addrs == NULL) {
(void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
free(buf);
(void) close(fd);
return (0);
}
ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
ifc.ifc_buf = buf;
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) {
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);
}
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);
}
static boolean_t
_read_nsw_file(void)
{
char defval[LINESIZE];
FILE *defl;
boolean_t nosort = B_FALSE;
do {
defl = fopen(__NSW_DEFAULT_FILE, "rF");
} while ((defl == NULL) && (errno == EINTR));
if (defl == NULL)
return (B_FALSE);
while (fgets(defval, sizeof (defval), defl) != NULL) {
if ((strncmp(DONT_SORT, defval, sizeof (DONT_SORT) - 1) == 0) ||
(strncmp(DONT_SORT2, defval,
sizeof (DONT_SORT2) - 1) == 0)) {
nosort = B_TRUE;
break;
}
}
(void) fclose(defl);
return (nosort);
}