#include "mt.h"
#include "../rpc/rpc_mt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <netconfig.h>
#include <netdir.h>
#include <syslog.h>
#include "yp_b.h"
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#include <sys/tiuser.h>
#define BFSIZE (YPMAXDOMAIN + 32)
int __ypipbufsize = 8192;
extern int getdomainname(char *, int);
static CLIENT *getclnt(rpcprog_t, rpcvers_t, struct netconfig *, int *);
static struct dom_binding *load_dom_binding(struct ypbind_resp *, char *,
int *);
static ypbind_resp *get_cached_domain(char *);
static int get_cached_transport(struct netconfig *, int, char *, int);
static int ypbind_running(int, int);
static void set_rdev(struct dom_binding *);
static int check_rdev(struct dom_binding *);
static char nullstring[] = "";
#define YPSLEEPTIME 5
unsigned int _ypsleeptime = YPSLEEPTIME;
#ifdef DEBUG
#define YPTIMEOUT 120
#define YPINTER_TRY 60
#else
#define YPTIMEOUT 20
#define YPINTER_TRY 5
#endif
#define MAX_TRIES_FOR_NEW_YP 1
struct timeval _ypserv_timeout = {
YPTIMEOUT,
0
};
static mutex_t default_domain_lock = DEFAULTMUTEX;
static char *default_domain;
static mutex_t bound_domains_lock = DEFAULTMUTEX;
static struct dom_binding *bound_domains;
void
free_dom_binding(struct dom_binding *p)
{
if (p->ref_count != 0) {
p->need_free = 1;
return;
}
(void) check_rdev(p);
clnt_destroy(p->dom_client);
free(p->dom_domain);
free(p);
}
static void
__yp_unbind_nolock(char *domain)
{
struct dom_binding *p;
struct dom_binding **prev;
if ((domain == NULL) || (strlen(domain) == 0)) {
return;
}
for (prev = &bound_domains; (p = *prev) != 0; prev = &p->dom_pnext) {
if (strcmp(domain, p->dom_domain) == 0) {
if (!p->cache_bad) {
p->cache_bad = 1;
break;
}
*prev = p->dom_pnext;
free_dom_binding(p);
break;
}
}
}
void
yp_unbind(char *domain)
{
(void) mutex_lock(&bound_domains_lock);
__yp_unbind_nolock(domain);
(void) mutex_unlock(&bound_domains_lock);
}
static void
newborn(void)
{
static pid_t mypid;
pid_t testpid;
struct dom_binding *p, *q;
if ((testpid = getpid()) != mypid) {
mypid = testpid;
for (p = bound_domains; p != 0; p = q) {
q = p->dom_pnext;
free_dom_binding(p);
}
bound_domains = 0;
}
}
static bool
check_binding(char *domain, struct dom_binding **binding)
{
struct dom_binding *pdomb;
struct ypbind_resp *ypbind_resp;
int status;
for (pdomb = bound_domains; pdomb != NULL; pdomb = pdomb->dom_pnext) {
if (strcmp(domain, pdomb->dom_domain) == 0) {
*binding = pdomb;
return (TRUE);
}
}
if ((ypbind_resp = get_cached_domain(domain)) != 0) {
pdomb = load_dom_binding(ypbind_resp, domain, &status);
if (pdomb == 0)
return (FALSE);
*binding = pdomb;
return (TRUE);
}
return (FALSE);
}
#define SOCKADDR_SIZE (sizeof (struct sockaddr_in6))
static int
__yp_add_binding_netid(char *domain, char *addr, char *netid)
{
struct netconfig *nconf = 0;
struct netbuf *svcaddr = 0;
struct ypbind_binding *binding = 0;
int status;
struct ypbind_resp resp;
struct dom_binding *pdomb;
nconf = getnetconfigent(netid);
if (nconf == 0)
goto err;
svcaddr = malloc(sizeof (struct netbuf));
if (svcaddr == 0)
goto err;
svcaddr->maxlen = SOCKADDR_SIZE;
svcaddr->buf = malloc(SOCKADDR_SIZE);
if (svcaddr->buf == 0)
goto err;
if (!rpcb_getaddr(YPPROG, YPVERS, nconf, svcaddr, addr))
goto err;
binding = malloc(sizeof (struct ypbind_binding));
if (binding == 0)
goto err;
binding->ypbind_hi_vers = YPVERS;
binding->ypbind_lo_vers = YPVERS;
binding->ypbind_nconf = nconf;
binding->ypbind_svcaddr = svcaddr;
binding->ypbind_servername = (char *)strdup(addr);
if (binding->ypbind_servername == 0)
goto err;
resp.ypbind_status = YPBIND_SUCC_VAL;
resp.ypbind_resp_u.ypbind_bindinfo = binding;
(void) mutex_lock(&bound_domains_lock);
newborn();
pdomb = load_dom_binding(&resp, domain, &status);
(void) mutex_unlock(&bound_domains_lock);
return (pdomb != 0);
err:
if (nconf)
freenetconfigent(nconf);
if (svcaddr) {
if (svcaddr->buf)
free(svcaddr->buf);
free(svcaddr);
}
if (binding) {
if (binding->ypbind_servername)
free(binding->ypbind_servername);
free(binding);
}
return (0);
}
int
__yp_add_binding(char *domain, char *addr) {
int ret = __yp_add_binding_netid(domain, addr, "udp6");
if (ret == 0)
ret = __yp_add_binding_netid(domain, addr, "udp");
return (ret);
}
static struct dom_binding *
load_dom_binding(struct ypbind_resp *ypbind_res, char *domain, int *err)
{
int fd;
struct dom_binding *pdomb;
pdomb = NULL;
if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) {
syslog(LOG_ERR, "load_dom_binding: malloc failure.");
*err = YPERR_RESRC;
return (NULL);
}
pdomb->dom_binding = ypbind_res->ypbind_resp_u.ypbind_bindinfo;
pdomb->dom_client = clnt_tli_create(RPC_ANYFD,
pdomb->dom_binding->ypbind_nconf,
pdomb->dom_binding->ypbind_svcaddr,
YPPROG, YPVERS, __ypipbufsize,
__ypipbufsize);
if (pdomb->dom_client == NULL) {
clnt_pcreateerror("yp_bind: clnt_tli_create");
free(pdomb);
*err = YPERR_RPC;
return (NULL);
}
#ifdef DEBUG
(void) printf("yp_bind: clnt_tli_create suceeded\n");
#endif
pdomb->dom_pnext = bound_domains;
pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1);
if (pdomb->dom_domain == NULL) {
clnt_destroy(pdomb->dom_client);
free(pdomb);
*err = YPERR_RESRC;
return (NULL);
}
pdomb->cache_bad = 0;
set_rdev(pdomb);
if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd))
(void) fcntl(fd, F_SETFD, 1);
(void) strcpy(pdomb->dom_domain, domain);
pdomb->ref_count = 0;
pdomb->need_free = 0;
(void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0);
bound_domains = pdomb;
return (pdomb);
}
static int
tli_open_rsvdport(struct netconfig *nconf)
{
int fd;
if (nconf == NULL)
return (-1);
fd = t_open(nconf->nc_device, O_RDWR, NULL);
if (fd == -1)
return (-1);
if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL) == -1) {
if (t_bind(fd, NULL, NULL) == -1) {
(void) t_close(fd);
return (-1);
}
}
return (fd);
}
static struct dom_binding *
load_dom_binding_rsvdport(struct ypbind_binding *dom_binding, char *domain,
int *err)
{
struct dom_binding *pdomb;
int fd;
pdomb = NULL;
if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) {
syslog(LOG_ERR, "load_dom_binding_rsvdport: malloc failure.");
*err = YPERR_RESRC;
return (NULL);
}
pdomb->dom_binding = dom_binding;
fd = tli_open_rsvdport(pdomb->dom_binding->ypbind_nconf);
if (fd < 0) {
clnt_pcreateerror("yp_bind: tli_open_rsvdport");
free(pdomb);
*err = YPERR_RPC;
return (NULL);
}
pdomb->dom_client = clnt_tli_create(fd,
pdomb->dom_binding->ypbind_nconf,
pdomb->dom_binding->ypbind_svcaddr,
YPPROG, YPVERS, __ypipbufsize,
__ypipbufsize);
if (pdomb->dom_client == NULL) {
clnt_pcreateerror("yp_bind: clnt_tli_create");
free(pdomb);
*err = YPERR_RPC;
return (NULL);
}
#ifdef DEBUG
(void) printf("yp_bind: clnt_tli_create suceeded\n");
#endif
(void) CLNT_CONTROL(pdomb->dom_client, CLSET_FD_CLOSE, NULL);
pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1);
if (pdomb->dom_domain == NULL) {
clnt_destroy(pdomb->dom_client);
free(pdomb);
*err = YPERR_RESRC;
return (NULL);
}
(void) strcpy(pdomb->dom_domain, domain);
pdomb->ref_count = 0;
pdomb->need_free = 0;
set_rdev(pdomb);
(void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0);
return (pdomb);
}
int
__yp_dobind_cflookup(
char *domain,
struct dom_binding **binding,
int hardlookup)
{
struct dom_binding *pdomb;
struct ypbind_resp *ypbind_resp;
struct ypbind_domain ypbd;
int status, err = YPERR_DOMAIN;
int first_try = 1;
CLIENT *tb = NULL;
if ((domain == NULL) ||(strlen(domain) == 0))
return (YPERR_BADARGS);
(void) mutex_lock(&bound_domains_lock);
newborn();
if (check_binding(domain, binding)) {
if (!(*binding)->cache_bad && check_rdev(*binding)) {
(*binding)->ref_count += 1;
(void) mutex_unlock(&bound_domains_lock);
return (0);
}
if ((*binding)->cache_bad) {
__yp_unbind_nolock(domain);
} else {
(*binding)->cache_bad = 1;
(void) mutex_unlock(&bound_domains_lock);
yp_unbind(domain);
(void) mutex_lock(&bound_domains_lock);
if (check_binding(domain, binding)) {
(*binding)->ref_count += 1;
(void) mutex_unlock(&bound_domains_lock);
return (0);
}
}
}
do {
if (first_try)
first_try = 0;
else {
(void) sleep(_ypsleeptime);
}
tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err);
if (tb == NULL) {
if (ypbind_running(err, rpc_createerr.cf_stat))
continue;
break;
}
ypbd.ypbind_domainname = domain;
ypbd.ypbind_vers = YPVERS;
ypbind_resp = ypbindproc_domain_3(&ypbd, tb);
if (ypbind_resp == NULL) {
clnt_perror(tb,
"ypbindproc_domain_3: can't contact ypbind");
clnt_destroy(tb);
tb = NULL;
continue;
}
if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) {
pdomb = load_dom_binding(ypbind_resp, domain, &status);
if (pdomb == 0) {
err = status;
clnt_destroy(tb);
tb = NULL;
continue;
}
clnt_destroy(tb);
pdomb->ref_count += 1;
(void) mutex_unlock(&bound_domains_lock);
*binding = pdomb;
return (0);
}
if (ypbind_resp->ypbind_resp_u.ypbind_error ==
YPBIND_ERR_NOSERV)
err = YPERR_DOMAIN;
else
err = YPERR_YPBIND;
clnt_destroy(tb);
tb = NULL;
} while (hardlookup);
if (tb != NULL)
clnt_destroy(tb);
(void) mutex_unlock(&bound_domains_lock);
if (err)
return (err);
return (YPERR_DOMAIN);
}
int
__yp_dobind(
char *domain,
struct dom_binding **binding)
{
return (__yp_dobind_cflookup(domain, binding, 1));
}
void
__yp_rel_binding(struct dom_binding *binding)
{
(void) mutex_lock(&bound_domains_lock);
binding->ref_count -= 1;
if (binding->need_free && binding->ref_count == 0)
free_dom_binding(binding);
(void) mutex_unlock(&bound_domains_lock);
}
int
__yp_dobind_rsvdport_cflookup(
char *domain,
struct dom_binding **binding,
int hardlookup)
{
struct dom_binding *pdomb;
struct ypbind_resp *ypbind_resp;
struct ypbind_domain ypbd;
int status, err = YPERR_DOMAIN;
int first_try = 1;
CLIENT *tb = NULL;
if ((domain == NULL) ||(strlen(domain) == 0))
return (YPERR_BADARGS);
(void) mutex_lock(&bound_domains_lock);
newborn();
if (check_binding(domain, binding)) {
if ((*binding)->cache_bad) {
__yp_unbind_nolock(domain);
} else {
pdomb = load_dom_binding_rsvdport(
(*binding)->dom_binding,
domain, &status);
if (pdomb == 0) {
(void) mutex_unlock(&bound_domains_lock);
return (status);
}
pdomb->ref_count += 1;
(void) mutex_unlock(&bound_domains_lock);
*binding = pdomb;
return (0);
}
}
do {
if (first_try)
first_try = 0;
else {
(void) sleep(_ypsleeptime);
}
tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err);
if (tb == NULL) {
if (ypbind_running(err, rpc_createerr.cf_stat))
continue;
break;
}
ypbd.ypbind_domainname = domain;
ypbd.ypbind_vers = YPVERS;
ypbind_resp = ypbindproc_domain_3(&ypbd, tb);
if (ypbind_resp == NULL) {
clnt_perror(tb,
"ypbindproc_domain_3: can't contact ypbind");
clnt_destroy(tb);
tb = NULL;
continue;
}
if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) {
pdomb = load_dom_binding_rsvdport(
ypbind_resp->ypbind_resp_u.ypbind_bindinfo,
domain, &status);
if (pdomb == 0) {
err = status;
clnt_destroy(tb);
tb = NULL;
continue;
}
clnt_destroy(tb);
pdomb->ref_count += 1;
(void) mutex_unlock(&bound_domains_lock);
*binding = pdomb;
return (0);
}
if (ypbind_resp->ypbind_resp_u.ypbind_error ==
YPBIND_ERR_NOSERV)
err = YPERR_DOMAIN;
else
err = YPERR_YPBIND;
clnt_destroy(tb);
tb = NULL;
} while (hardlookup);
if (tb != NULL)
clnt_destroy(tb);
(void) mutex_unlock(&bound_domains_lock);
if (err)
return (err);
return (YPERR_DOMAIN);
}
int
__yp_dobind_rsvdport(
char *domain,
struct dom_binding **binding)
{
return (__yp_dobind_rsvdport_cflookup(domain, binding, 1));
}
int
yp_bind(char *domain)
{
struct dom_binding *binding;
int res;
res = __yp_dobind(domain, &binding);
if (res == 0)
__yp_rel_binding(binding);
return (res);
}
static char *
__default_domain(void)
{
char temp[256];
(void) mutex_lock(&default_domain_lock);
if (default_domain) {
(void) mutex_unlock(&default_domain_lock);
return (default_domain);
}
if (getdomainname(temp, sizeof (temp)) < 0) {
(void) mutex_unlock(&default_domain_lock);
return (0);
}
if (strlen(temp) > 0) {
default_domain = malloc((strlen(temp) + 1));
if (default_domain == 0) {
(void) mutex_unlock(&default_domain_lock);
return (0);
}
(void) strcpy(default_domain, temp);
(void) mutex_unlock(&default_domain_lock);
return (default_domain);
}
(void) mutex_unlock(&default_domain_lock);
return (0);
}
int
yp_get_default_domain(char **domain)
{
if ((*domain = __default_domain()) != 0)
return (0);
return (YPERR_YPERR);
}
int
usingypmap(char **ddn, char *map)
{
char in, *outval = NULL;
int outvallen, stat;
char *domain;
if ((domain = __default_domain()) == 0)
return (FALSE);
*ddn = domain;
in = (char)0xff;
stat = yp_match(domain, map, &in, 1, &outval, &outvallen);
if (outval != NULL)
free(outval);
switch (stat) {
case 0:
case YPERR_KEY:
case YPERR_NOMORE:
case YPERR_BUSY:
return (TRUE);
}
return (FALSE);
}
CLIENT *
__clnt_create_loopback(rpcprog_t prog, rpcvers_t vers, int *err)
{
struct netconfig *nconf;
CLIENT *clnt = NULL;
void *nc_handle;
*err = 0;
nc_handle = setnetconfig();
if (nc_handle == NULL) {
rpc_createerr.cf_stat = RPC_FAILED;
*err = YPERR_RPC;
return (NULL);
}
while (nconf = getnetconfig(nc_handle))
if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) &&
((nconf->nc_semantics == NC_TPI_COTS) ||
(nconf->nc_semantics == NC_TPI_COTS_ORD))) {
clnt = getclnt(prog, vers, nconf, err);
break;
}
(void) endnetconfig(nc_handle);
if (clnt == NULL) {
if (rpc_createerr.cf_stat == 0)
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
if (*err == 0) *err = YPERR_RPC;
}
return (clnt);
}
static CLIENT *
getclnt(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, int *err)
{
int fd;
struct netbuf *svcaddr;
CLIENT *cl;
struct nd_addrlist *nas;
struct nd_hostserv rpcbind_hs;
struct t_call sndcall;
char uaddress[1024];
RPCB parms;
enum clnt_stat clnt_st;
char *ua;
struct timeval tv = { 30, 0 };
if (nconf == NULL) {
rpc_createerr.cf_stat = RPC_TLIERROR;
*err = YPERR_RPC;
return (NULL);
}
if (get_cached_transport(nconf, vers, uaddress, sizeof (uaddress))) {
goto create_client;
}
rpcbind_hs.h_host = HOST_SELF_CONNECT;
rpcbind_hs.h_serv = "rpcbind";
if (netdir_getbyname(nconf, &rpcbind_hs, &nas) != ND_OK) {
rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
*err = YPERR_RPC;
return (NULL);
}
if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) {
rpc_createerr.cf_stat = RPC_TLIERROR;
*err = YPERR_RPC;
return (NULL);
}
if (t_bind(fd, NULL, NULL) == -1) {
rpc_createerr.cf_stat = RPC_TLIERROR;
*err = YPERR_RPC;
(void) t_close(fd);
return (NULL);
}
sndcall.addr = *(nas->n_addrs);
sndcall.opt.len = 0;
sndcall.udata.len = 0;
if (t_connect(fd, &sndcall, NULL) == -1) {
netdir_free((char *)nas, ND_ADDRLIST);
rpc_createerr.cf_stat = RPC_TLIERROR;
(void) t_close(fd);
*err = YPERR_PMAP;
return (NULL);
}
cl = clnt_tli_create(fd, nconf, nas->n_addrs,
RPCBPROG, RPCBVERS, __ypipbufsize, __ypipbufsize);
netdir_free((char *)nas, ND_ADDRLIST);
if (cl == NULL) {
(void) t_close(fd);
*err = YPERR_PMAP;
return (NULL);
}
parms.r_prog = prog;
parms.r_vers = vers;
parms.r_netid = nconf->nc_netid;
parms.r_addr = nullstring;
parms.r_owner = nullstring;
ua = uaddress;
clnt_st = CLNT_CALL(cl, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms,
xdr_wrapstring, (char *)&ua, tv);
(void) t_close(fd);
clnt_destroy(cl);
if (clnt_st != RPC_SUCCESS) {
*err = YPERR_YPBIND;
return (NULL);
}
if (strlen(uaddress) == 0) {
*err = YPERR_YPBIND;
rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
return (NULL);
}
create_client:
svcaddr = uaddr2taddr(nconf, uaddress);
cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, prog, vers,
__ypipbufsize, __ypipbufsize);
netdir_free((char *)svcaddr, ND_ADDR);
if (cl == NULL) {
*err = YPERR_YPBIND;
return (NULL);
}
return (cl);
}
static int
get_cached_transport(struct netconfig *nconf, int vers, char *uaddress,
int ulen)
{
ssize_t st;
int fd;
(void) snprintf(uaddress, ulen,
"%s/xprt.%s.%d", BINDING, nconf->nc_netid, vers);
fd = open(uaddress, O_RDONLY);
if (fd == -1)
return (0);
st = lockf(fd, F_TEST, 1);
if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
(void) close(fd);
return (0);
}
st = read(fd, uaddress, ulen);
if (st == -1) {
(void) close(fd);
return (0);
}
(void) close(fd);
return (1);
}
static ypbind_resp *
get_cached_domain(char *domain)
{
FILE *fp;
int st;
char filename[300];
static ypbind_resp res;
XDR xdrs;
(void) snprintf(filename, sizeof (filename),
"%s/%s/cache_binding", BINDING, domain);
fp = fopen(filename, "rF");
if (fp == 0)
return (0);
st = lockf(fileno(fp), F_TEST, 1);
if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
(void) fclose(fp);
return (0);
}
xdrstdio_create(&xdrs, fp, XDR_DECODE);
(void) memset((char *)&res, 0, sizeof (res));
st = xdr_ypbind_resp(&xdrs, &res);
xdr_destroy(&xdrs);
(void) fclose(fp);
if (st)
return (&res);
return (0);
}
static int
ypbind_running(int err, int status)
{
char filename[300];
int st;
int fd;
(void) snprintf(filename, sizeof (filename), "%s/ypbind.pid", BINDING);
fd = open(filename, O_RDONLY);
if (fd == -1) {
if ((err == YPERR_YPBIND) && (status != RPC_PROGNOTREGISTERED))
return (1);
return (0);
}
st = lockf(fd, F_TEST, 1);
if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
(void) close(fd);
return (0);
}
(void) close(fd);
return (1);
}
static void
set_rdev(struct dom_binding *pdomb)
{
int fd;
struct stat stbuf;
if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd) != TRUE ||
fstat(fd, &stbuf) == -1) {
syslog(LOG_DEBUG, "ypbind client: can't get rdev");
pdomb->fd = -1;
return;
}
pdomb->fd = fd;
pdomb->rdev = stbuf.st_rdev;
}
static int
check_rdev(struct dom_binding *pdomb)
{
struct stat stbuf;
if (pdomb->fd == -1)
return (1);
if (fstat(pdomb->fd, &stbuf) == -1) {
syslog(LOG_DEBUG, "yp_bind client: can't stat %d", pdomb->fd);
clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL);
return (0);
}
if (pdomb->rdev != stbuf.st_rdev) {
syslog(LOG_DEBUG,
"yp_bind client: fd %d changed, old=0x%x, new=0x%x",
pdomb->fd, pdomb->rdev, stbuf.st_rdev);
clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL);
return (0);
}
return (1);
}