#include "mt.h"
#include "../rpc/rpc_mt.h"
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <tiuser.h>
#include <netdir.h>
#include <netconfig.h>
#include <string.h>
#include <sys/file.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <malloc.h>
#include <syslog.h>
#include <nss_netdir.h>
#include <netinet/in.h>
#include <netdb.h>
extern const char __nsl_dom[];
extern char *dgettext(const char *, const char *);
struct translator {
struct nd_addrlist *(*gbn)();
struct nd_hostservlist *(*gba)();
int (*opt)();
char *(*t2u)();
struct netbuf *(*u2t)();
void *tr_fd;
char *tr_name;
struct translator *next;
};
static struct translator *xlate_list = NULL;
static mutex_t xlate_lock = DEFAULTMUTEX;
static struct translator *load_xlate(char *);
#undef _nderror
int _nderror;
int *
__nderror(void)
{
static pthread_key_t nderror_key = PTHREAD_ONCE_KEY_NP;
int *ret;
if (thr_main())
return (&_nderror);
ret = thr_get_storage(&nderror_key, sizeof (int), free);
return (ret ? ret : &_nderror);
}
#define _nderror (*(__nderror()))
void
add_to_xlate_list(struct translator *translate)
{
struct translator *t;
for (t = xlate_list; t; t = t->next) {
if (strcmp(translate->tr_name, t->tr_name) == 0) {
return;
}
}
translate->next = xlate_list;
xlate_list = translate;
}
int
netdir_getbyname(struct netconfig *tp, struct nd_hostserv *serv,
struct nd_addrlist **addrs)
{
if (tp == 0) {
_nderror = ND_BADARG;
return (_nderror);
}
if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
(tp->nc_nlookups == 0)) {
struct nss_netdirbyname_in nssin;
union nss_netdirbyname_out nssout;
nssin.op_t = NETDIR_BY;
nssin.arg.nd_hs = serv;
nssin.arg.nss.host6.af_family = AF_INET;
nssin.arg.nss.host6.flags = 0;
nssout.nd_alist = addrs;
return (_get_hostserv_inetnetdir_byname(tp, &nssin, &nssout));
}
if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
(tp->nc_nlookups == 0)) {
struct nss_netdirbyname_in nssin;
union nss_netdirbyname_out nssout;
nssin.op_t = NETDIR_BY6;
nssin.arg.nd_hs = serv;
nssin.arg.nss.host6.af_family = AF_INET6;
nssin.arg.nss.host6.flags = (AI_ALL | AI_V4MAPPED);
nssout.nd_alist = addrs;
return (_get_hostserv_inetnetdir_byname(tp, &nssin, &nssout));
}
return (__classic_netdir_getbyname(tp, serv, addrs));
}
int
__classic_netdir_getbyname(struct netconfig *tp, struct nd_hostserv *serv,
struct nd_addrlist **addrs)
{
struct translator *t;
struct nd_addrlist *nn;
char *lr;
int i;
_nderror = ND_SYSTEM;
for (i = 0; i < tp->nc_nlookups; i++) {
lr = *((tp->nc_lookups) + i);
for (t = xlate_list; t; t = t->next) {
if (strcmp(lr, t->tr_name) == 0) {
nn = (*(t->gbn))(tp, serv);
if (nn) {
*addrs = nn;
return (0);
}
if (_nderror < 0) {
return (_nderror);
}
break;
}
}
if (!t) {
if ((t = load_xlate(lr)) != NULL) {
(void) mutex_lock(&xlate_lock);
add_to_xlate_list(t);
(void) mutex_unlock(&xlate_lock);
nn = (*(t->gbn))(tp, serv);
if (nn) {
*addrs = nn;
return (0);
}
if (_nderror < 0) {
return (_nderror);
}
} else {
if (_nderror == ND_SYSTEM) {
_nderror = ND_OK;
i--;
continue;
}
}
}
}
return (_nderror);
}
int
netdir_getbyaddr(struct netconfig *tp, struct nd_hostservlist **serv,
struct netbuf *addr)
{
if (tp == 0) {
_nderror = ND_BADARG;
return (_nderror);
}
if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
(tp->nc_nlookups == 0)) {
struct nss_netdirbyaddr_in nssin;
union nss_netdirbyaddr_out nssout;
nssin.op_t = NETDIR_BY;
nssin.arg.nd_nbuf = addr;
nssout.nd_hslist = serv;
return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
}
if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
(tp->nc_nlookups == 0)) {
struct nss_netdirbyaddr_in nssin;
union nss_netdirbyaddr_out nssout;
nssin.op_t = NETDIR_BY6;
nssin.arg.nd_nbuf = addr;
nssout.nd_hslist = serv;
return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
}
return (__classic_netdir_getbyaddr(tp, serv, addr));
}
int
__netdir_getbyaddr_nosrv(struct netconfig *tp, struct nd_hostservlist **serv,
struct netbuf *addr)
{
if (tp == 0) {
_nderror = ND_BADARG;
return (_nderror);
}
if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
(tp->nc_nlookups == 0)) {
struct nss_netdirbyaddr_in nssin;
union nss_netdirbyaddr_out nssout;
nssin.op_t = NETDIR_BY_NOSRV;
nssin.arg.nd_nbuf = addr;
nssout.nd_hslist = serv;
return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
}
if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
(tp->nc_nlookups == 0)) {
struct nss_netdirbyaddr_in nssin;
union nss_netdirbyaddr_out nssout;
nssin.op_t = NETDIR_BY_NOSRV6;
nssin.arg.nd_nbuf = addr;
nssout.nd_hslist = serv;
return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
}
return (__classic_netdir_getbyaddr(tp, serv, addr));
}
int
__classic_netdir_getbyaddr(struct netconfig *tp, struct nd_hostservlist **serv,
struct netbuf *addr)
{
struct translator *t;
struct nd_hostservlist *hs;
char *lr;
int i;
_nderror = ND_SYSTEM;
for (i = 0; i < tp->nc_nlookups; i++) {
lr = *((tp->nc_lookups) + i);
for (t = xlate_list; t; t = t->next) {
if (strcmp(lr, t->tr_name) == 0) {
hs = (*(t->gba))(tp, addr);
if (hs) {
*serv = hs;
return (0);
}
if (_nderror < 0)
return (_nderror);
break;
}
}
if (!t) {
if ((t = load_xlate(lr)) != NULL) {
(void) mutex_lock(&xlate_lock);
add_to_xlate_list(t);
(void) mutex_unlock(&xlate_lock);
hs = (*(t->gba))(tp, addr);
if (hs) {
*serv = hs;
return (0);
}
if (_nderror < 0)
return (_nderror);
} else {
if (_nderror == ND_SYSTEM) {
_nderror = ND_OK;
i--;
continue;
}
}
}
}
return (_nderror);
}
int
netdir_options(struct netconfig *tp, int option, int fd, char *par)
{
struct translator *t;
char *lr;
int i;
if (tp == 0) {
_nderror = ND_BADARG;
return (_nderror);
}
if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
(tp->nc_nlookups == 0)) {
return (__inet_netdir_options(tp, option, fd, par));
}
for (i = 0; i < tp->nc_nlookups; i++) {
lr = *((tp->nc_lookups) + i);
for (t = xlate_list; t; t = t->next) {
if (strcmp(lr, t->tr_name) == 0)
return ((*(t->opt))(tp, option, fd, par));
}
if (!t) {
if ((t = load_xlate(lr)) != NULL) {
(void) mutex_lock(&xlate_lock);
add_to_xlate_list(t);
(void) mutex_unlock(&xlate_lock);
return ((*(t->opt))(tp, option, fd, par));
}
if (_nderror == ND_SYSTEM) {
_nderror = ND_OK;
i--;
continue;
}
}
}
return (_nderror);
}
struct netbuf *
uaddr2taddr(struct netconfig *tp, char *addr)
{
struct translator *t;
struct netbuf *x;
char *lr;
int i;
if (tp == 0) {
_nderror = ND_BADARG;
return (0);
}
if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
(tp->nc_nlookups == 0)) {
return (__inet_uaddr2taddr(tp, addr));
}
for (i = 0; i < tp->nc_nlookups; i++) {
lr = *((tp->nc_lookups) + i);
for (t = xlate_list; t; t = t->next) {
if (strcmp(lr, t->tr_name) == 0) {
x = (*(t->u2t))(tp, addr);
if (x)
return (x);
if (_nderror < 0)
return (0);
}
}
if (!t) {
if ((t = load_xlate(lr)) != NULL) {
(void) mutex_lock(&xlate_lock);
add_to_xlate_list(t);
(void) mutex_unlock(&xlate_lock);
x = (*(t->u2t))(tp, addr);
if (x)
return (x);
if (_nderror < 0)
return (0);
} else {
if (_nderror == ND_SYSTEM) {
_nderror = ND_OK;
i--;
continue;
}
}
}
}
return (0);
}
char *
taddr2uaddr(struct netconfig *tp, struct netbuf *addr)
{
struct translator *t;
char *lr;
char *x;
int i;
if (tp == 0) {
_nderror = ND_BADARG;
return (0);
}
if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
(tp->nc_nlookups == 0)) {
return (__inet_taddr2uaddr(tp, addr));
}
for (i = 0; i < tp->nc_nlookups; i++) {
lr = *((tp->nc_lookups) + i);
for (t = xlate_list; t; t = t->next) {
if (strcmp(lr, t->tr_name) == 0) {
x = (*(t->t2u))(tp, addr);
if (x)
return (x);
if (_nderror < 0)
return (0);
}
}
if (!t) {
if ((t = load_xlate(lr)) != NULL) {
(void) mutex_lock(&xlate_lock);
add_to_xlate_list(t);
(void) mutex_unlock(&xlate_lock);
x = (*(t->t2u))(tp, addr);
if (x)
return (x);
if (_nderror < 0)
return (0);
} else {
if (_nderror == ND_SYSTEM) {
_nderror = ND_OK;
i--;
continue;
}
}
}
}
return (0);
}
void
netdir_free(void *ptr, int type)
{
struct netbuf *na;
struct nd_addrlist *nas;
struct nd_hostserv *hs;
struct nd_hostservlist *hss;
int i;
if (ptr == NULL)
return;
switch (type) {
case ND_ADDR :
na = (struct netbuf *)ptr;
if (na->buf)
free(na->buf);
free(na);
break;
case ND_ADDRLIST :
nas = (struct nd_addrlist *)ptr;
if (nas->n_addrs->buf)
free(nas->n_addrs->buf);
free(nas->n_addrs);
free(nas);
break;
case ND_HOSTSERV :
hs = (struct nd_hostserv *)ptr;
if (hs->h_host)
free(hs->h_host);
if (hs->h_serv)
free(hs->h_serv);
free(hs);
break;
case ND_HOSTSERVLIST :
hss = (struct nd_hostservlist *)ptr;
for (hs = hss->h_hostservs, i = 0; i < hss->h_cnt; i++, hs++) {
if (hs->h_host)
free(hs->h_host);
if (hs->h_serv)
free(hs->h_serv);
}
free(hss->h_hostservs);
free(hss);
break;
default :
_nderror = ND_UKNWN;
break;
}
}
static struct translator *
load_xlate(char *name)
{
struct translator *t;
static struct xlate_list {
char *library;
struct xlate_list *next;
} *xlistp = NULL;
struct xlate_list *xlp, **xlastp;
static mutex_t xlist_lock = DEFAULTMUTEX;
(void) mutex_lock(&xlist_lock);
for (xlp = xlistp, xlastp = &xlistp; xlp != NULL;
xlastp = &xlp->next, xlp = xlp->next) {
if (xlp->library != NULL) {
if (strcmp(xlp->library, name) == 0) {
_nderror = ND_SYSTEM;
(void) mutex_unlock(&xlist_lock);
return (0);
}
}
}
t = malloc(sizeof (struct translator));
if (!t) {
_nderror = ND_NOMEM;
(void) mutex_unlock(&xlist_lock);
return (0);
}
t->tr_name = strdup(name);
if (!t->tr_name) {
_nderror = ND_NOMEM;
free(t);
(void) mutex_unlock(&xlist_lock);
return (NULL);
}
t->tr_fd = dlopen(name, RTLD_LAZY);
if (t->tr_fd == NULL) {
_nderror = ND_OPEN;
goto error;
}
t->gbn = (struct nd_addrlist *(*)())dlsym(t->tr_fd,
"_netdir_getbyname");
if (!(t->gbn)) {
_nderror = ND_NOSYM;
goto error;
}
t->gba = (struct nd_hostservlist *(*)())dlsym(t->tr_fd,
"_netdir_getbyaddr");
if (!(t->gba)) {
_nderror = ND_NOSYM;
goto error;
}
t->t2u = (char *(*)())dlsym(t->tr_fd, "_taddr2uaddr");
if (!(t->t2u)) {
_nderror = ND_NOSYM;
goto error;
}
t->u2t = (struct netbuf *(*)())dlsym(t->tr_fd, "_uaddr2taddr");
if (!(t->u2t)) {
_nderror = ND_NOSYM;
goto error;
}
t->opt = (int (*)())dlsym(t->tr_fd, "_netdir_options");
if (!(t->opt)) {
_nderror = ND_NOSYM;
goto error;
}
*xlastp = malloc(sizeof (struct xlate_list));
if (*xlastp == NULL) {
_nderror = ND_NOMEM;
goto error;
}
(*xlastp)->library = strdup(name);
if ((*xlastp)->library == NULL) {
_nderror = ND_NOMEM;
free(*xlastp);
goto error;
}
(*xlastp)->next = NULL;
(void) mutex_unlock(&xlist_lock);
return (t);
error:
if (t->tr_fd != NULL)
(void) dlclose(t->tr_fd);
free(t->tr_name);
free(t);
(void) mutex_unlock(&xlist_lock);
return (NULL);
}
#define NDERR_BUFSZ 512
char *
netdir_sperror(void)
{
static pthread_key_t nderrbuf_key = PTHREAD_ONCE_KEY_NP;
static char buf_main[NDERR_BUFSZ];
char *str;
char *dlerrstr;
str = thr_main()?
buf_main :
thr_get_storage(&nderrbuf_key, NDERR_BUFSZ, free);
if (str == NULL)
return (NULL);
dlerrstr = dlerror();
switch (_nderror) {
case ND_NOMEM :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom, "n2a: memory allocation failed"));
break;
case ND_OK :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom, "n2a: successful completion"));
break;
case ND_NOHOST :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom, "n2a: hostname not found"));
break;
case ND_NOSERV :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom, "n2a: service name not found"));
break;
case ND_NOSYM :
(void) snprintf(str, NDERR_BUFSZ, "%s : %s ",
dgettext(__nsl_dom,
"n2a: symbol missing in shared object"),
dlerrstr ? dlerrstr : " ");
break;
case ND_OPEN :
(void) snprintf(str, NDERR_BUFSZ, "%s - %s ",
dgettext(__nsl_dom, "n2a: couldn't open shared object"),
dlerrstr ? dlerrstr : " ");
break;
case ND_ACCESS :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom,
"n2a: access denied for shared object"));
break;
case ND_UKNWN :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom,
"n2a: attempt to free unknown object"));
break;
case ND_BADARG :
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom,
"n2a: bad arguments passed to routine"));
break;
case ND_NOCTRL:
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom, "n2a: unknown option passed"));
break;
case ND_FAILCTRL:
(void) snprintf(str, NDERR_BUFSZ,
dgettext(__nsl_dom, "n2a: control operation failed"));
break;
case ND_SYSTEM:
(void) snprintf(str, NDERR_BUFSZ, "%s: %s",
dgettext(__nsl_dom, "n2a: system error"),
strerror(errno));
break;
default :
(void) snprintf(str, NDERR_BUFSZ, "%s#%d",
dgettext(__nsl_dom, "n2a: unknown error "), _nderror);
break;
}
return (str);
}
void
netdir_perror(char *s)
{
char *err;
err = netdir_sperror();
(void) fprintf(stderr, "%s: %s\n", s, err ? err : "n2a: error");
}