#include <libc.h>
#include <string.h>
#include <syslog.h>
#include <netinfo/ni.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <rpc/xdr.h>
#include <net/if.h>
#include <ctype.h>
#include <errno.h>
#include "clib.h"
#include "sys_interfaces.h"
#define LOCAL_PORT 1033
#define NI_TIMEOUT_SHORT 5
#define NI_TIMEOUT_LONG 60
#define NI_TRIES 5
#define NI_SLEEPTIME 4
#define NI_MAXSLEEPTIME 16
#define NI_MAXCONNTRIES 2
#define IS_BROADCASTADDR(addr) (((unsigned char *) &addr)[0] == 0xFF)
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK (u_long)0x7f000001
#endif
#define debug(msg) syslog(LOG_ERR, msg)
#define clnt_debug(ni, msg)
typedef struct ni_private
{
int naddrs;
struct in_addr *addrs;
int whichwrite;
ni_name *tags;
int pid;
int tsock;
int tport;
CLIENT *tc;
long tv_sec;
long rtv_sec;
long wtv_sec;
int abort;
int needwrite;
int uid;
ni_name passwd;
} ni_private;
#define NIP(ni) ((ni_private *)(ni))
#define RCALLIT(a, b, c) callit((ni_private *)(a), (void *(*)())(b), (void *)c, 0)
#define WCALLIT(a, b, c) callit((ni_private *)(a), (void *(*)())(b), (void *)c, 1)
static const ni_name NAME_NAME = "name";
static const ni_name NAME_SERVES = "serves";
static const ni_name NAME_MACHINES = "machines";
static const ni_name NAME_IP_ADDRESS = "ip_address";
static const ni_name NAME_MASTER = "master";
static const ni_name NAME_USERS = "users";
static const ni_name NAME_UID = "uid";
static const ni_name NAME_DOMAIN_SERVERS = "domain_servers";
typedef struct getreg_stuff {
nibind_getregister_res res;
ni_private *ni;
} getreg_stuff;
static int socket_open(struct sockaddr_in *raddr, int, int, int, int, int);
extern int bindresvport(int, struct sockaddr_in *);
static int
getmyport(int sock)
{
struct sockaddr_in sin;
int sinlen;
sinlen = sizeof(struct sockaddr_in);
if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != 0) return -1;
if (sin.sin_port == 0)
{
if (bind(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) != 0) return -1;
sinlen = sizeof(struct sockaddr_in);
if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != 0) return -1;
}
return ntohs(sin.sin_port);
}
static void
createauth(ni_private *ni)
{
if (ni->passwd != NULL && ni->tc != NULL)
{
auth_destroy(ni->tc->cl_auth);
ni->tc->cl_auth = authunix_create(ni->passwd, ni->uid, 0, 0, NULL);
}
}
static void
fixtimeout(struct timeval *tv, long sec, int tries)
{
tv->tv_sec = sec / tries;
tv->tv_usec = ((sec % tries) * 1000000) / tries;
}
static void
ni_settimeout(ni_private *ni, int timeout)
{
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
ni->tv_sec = timeout;
if (ni->tc != NULL) clnt_control(ni->tc, CLSET_TIMEOUT, (char *)&tv);
}
static int
connectit(ni_private *ni)
{
struct sockaddr_in sin;
int sock, islocal;
CLIENT *cl;
struct timeval tv;
enum clnt_stat stat;
nibind_getregister_res res;
interface_list_t *ilist;
sock = -1;
bzero(&sin, sizeof(sin));
sin.sin_port = 0;
sin.sin_family = AF_INET;
tv.tv_sec = ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec;
tv.tv_usec = 0;
ni_settimeout(ni, tv.tv_sec);
fixtimeout(&tv, ni->tv_sec, NI_TRIES);
islocal = 0;
if (!strcmp(ni->tags[0], "local"))
{
if (ni->addrs[0].s_addr == htonl(INADDR_LOOPBACK))
{
islocal = 1;
}
else
{
ilist = _libinfo_ni_sys_interfaces();
if (_libinfo_ni_sys_is_my_address(ilist, &ni->addrs[0])) islocal = 1;
_libinfo_ni_sys_interfaces_release(ilist);
}
if (islocal != 0)
{
sin.sin_port = htons(LOCAL_PORT);
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sock = socket_open(&sin, NI_PROG, NI_VERS, ni->tv_sec, NI_TRIES, IPPROTO_TCP);
}
}
if (sock < 0)
{
sin.sin_port = 0;
sin.sin_addr = ni->addrs[0];
sock = socket_open(&sin, NIBIND_PROG, NIBIND_VERS, ni->tv_sec, NI_TRIES, IPPROTO_UDP);
if (sock < 0) return 0;
cl = clntudp_create(&sin, NIBIND_PROG, NIBIND_VERS, tv, &sock);
if (cl == NULL)
{
close(sock);
return 0;
}
tv.tv_sec = ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec;
tv.tv_usec = 0;
stat = clnt_call(cl, NIBIND_GETREGISTER, (xdrproc_t)xdr_ni_name, (char *)&ni->tags[0], (xdrproc_t)xdr_nibind_getregister_res, (char *)&res, tv);
clnt_destroy(cl);
close(sock);
if (stat != RPC_SUCCESS || res.status != NI_OK) return 0;
sin.sin_port = htons(res.nibind_getregister_res_u.addrs.tcp_port);
sock = socket_open(&sin, NI_PROG, NI_VERS, ni->tv_sec, NI_TRIES, IPPROTO_TCP);
}
if (sock < 0) return 0;
cl = clnttcp_create(&sin, NI_PROG, NI_VERS, &sock, 0, 0);
if (cl == NULL)
{
close(sock);
return 0;
}
clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
ni->tc = cl;
ni->tsock = sock;
ni->tport = getmyport(sock);
createauth(ni);
fcntl(ni->tsock, F_SETFD, 1);
return 1;
}
void
ni_setabort(void *ni, int abort)
{
if (ni == NULL) return;
((ni_private *)ni)->abort = abort;
}
void
ni_setwritetimeout(void *ni, int timeout)
{
if (ni == NULL) return;
((ni_private *)ni)->wtv_sec = timeout;
}
void
ni_setreadtimeout(void *ni, int timeout)
{
if (ni == NULL) return;
((ni_private *)ni)->rtv_sec = timeout;
}
void
ni_needwrite(void *ni, int needwrite)
{
if (ni == NULL) return;
((ni_private *)ni)->needwrite = needwrite;
}
static int
connectlocal(ni_private *ni)
{
int printed = 0;
ni->naddrs = 1;
ni->addrs = (struct in_addr *)malloc(sizeof(struct in_addr));
ni->addrs[0].s_addr = htonl(INADDR_LOOPBACK);
ni->tags = (ni_name *)malloc(sizeof(ni_name));
ni->tags[0] = ni_name_dup("local");
ni->whichwrite = 0;
while (connectit(ni) == 0)
{
if (printed == 0)
{
syslog(LOG_ERR, "NetInfo timeout connecting to local domain, sleeping");
printed = 1;
}
sleep(NI_SLEEPTIME);
}
if (printed != 0) syslog(LOG_INFO, "NetInfo connection to local domain waking");
return 1;
}
static void
clnt_kill(CLIENT *cl, int sock, int port)
{
int save = 0;
int p;
p = getmyport(sock);
if ((sock >= 0) && (p != -1) && (p != port))
{
save = 1;
}
if (cl != NULL)
{
if (cl->cl_auth != NULL) auth_destroy(cl->cl_auth);
clnt_destroy(cl);
}
if (save == 0) close(sock);
}
static void
reinit(ni_private *ni)
{
if (ni == NULL) return;
if (ni->tc != NULL)
{
clnt_kill(ni->tc, ni->tsock, ni->tport);
ni->tc = NULL;
}
ni->tsock = -1;
ni->tport = -1;
ni->pid = getpid();
}
static void
ni_switch(ni_private *ni, ni_index which)
{
struct in_addr tmp_addr;
ni_name tmp_tag;
if (ni == NULL) return;
if (which == 0) return;
reinit(ni);
tmp_addr = ni->addrs[0];
tmp_tag = ni->tags[0];
ni->addrs[0] = ni->addrs[which];
ni->tags[0] = ni->tags[which];
ni->addrs[which] = tmp_addr;
ni->tags[which] = tmp_tag;
if (ni->whichwrite == 0) ni->whichwrite = which;
else if (ni->whichwrite == which) ni->whichwrite = 0;
}
static void
ni_swap(ni_private *ni, ni_index a, ni_index b)
{
struct in_addr tmp_addr;
ni_name tmp_tag;
if (a == b) return;
tmp_addr = ni->addrs[a];
tmp_tag = ni->tags[a];
ni->addrs[a] = ni->addrs[b];
ni->tags[a] = ni->tags[b];
ni->addrs[b] = tmp_addr;
ni->tags[b] = tmp_tag;
if (ni->whichwrite == a) ni->whichwrite = b;
else if (ni->whichwrite == b) ni->whichwrite = a;
}
static ni_status
ni_ping(unsigned short port, struct in_addr addr)
{
struct sockaddr_in sin;
int sock;
CLIENT *cl;
struct timeval timeout, retry;
enum clnt_stat stat;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = port;
sin.sin_addr = addr;
timeout.tv_sec = NI_TIMEOUT_SHORT;
timeout.tv_usec = 0;
retry.tv_sec = 1;
retry.tv_usec = 0;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) return NI_FAILED;
cl = clntudp_create(&sin, NI_PROG, NI_VERS, timeout, &sock);
if (cl == NULL)
{
close(sock);
return NI_FAILED;
}
clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&retry);
stat = clnt_call(cl, _NI_PING, (xdrproc_t)xdr_void, (char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, timeout);
clnt_destroy(cl);
close(sock);
if (stat != RPC_SUCCESS) return NI_FAILED;
return NI_OK;
}
static bool_t
eachresult(void *vstuff, struct sockaddr_in *sin, int which)
{
ni_status status;
getreg_stuff *stuff = (getreg_stuff *)vstuff;
if (stuff->res.status != NI_OK) return FALSE;
status = ni_ping(htons(stuff->res.nibind_getregister_res_u.addrs.udp_port), stuff->ni->addrs[which]);
if (status != NI_OK) return FALSE;
ni_switch(stuff->ni, which);
return TRUE;
}
static void
shuffle(ni_private *ni)
{
int *shuffle;
int i, j, rfd, rv, te;
unsigned int re;
static int initialized = 0;
if (ni == NULL) return;
if (ni->naddrs <= 1) return;
rfd = open("/dev/random", O_RDONLY, 0);
shuffle = (int *)malloc(ni->naddrs * sizeof(int));
for (i = 0; i < ni->naddrs; i++) shuffle[i] = i;
for (i = 0, j = ni->naddrs; j > 0; i++, j--)
{
if ((rfd < 0) || (read(rfd, &rv, sizeof(rv)) != sizeof(rv)))
{
if (initialized == 0)
{
srandom(gethostid() ^ time(NULL));
initialized = 1;
}
rv = random();
}
re = (unsigned int)rv % j;
te = shuffle[re];
shuffle[re] = shuffle[j-1];
shuffle[j-1] = te;
ni_swap(ni, re, j-1);
}
free(shuffle);
if (rfd > 0) close(rfd);
return;
}
static int
rebind(ni_private *ni)
{
enum clnt_stat stat;
getreg_stuff stuff;
int sleeptime = NI_SLEEPTIME;
int printed = 0;
int nlocal;
int nnetwork;
interface_list_t *ilist;
int i;
if (ni->naddrs == 1)
{
ni->whichwrite = 0;
return 1;
}
ilist = _libinfo_ni_sys_interfaces();
shuffle(ni);
nlocal = 0;
for (i = nlocal; i < ni->naddrs; i++)
{
if (_libinfo_ni_sys_is_my_address(ilist, &ni->addrs[i]))
{
ni_swap(ni, nlocal, i);
nlocal++;
}
}
nnetwork = nlocal;
for (i = nnetwork; i < ni->naddrs; i++)
{
if (_libinfo_ni_sys_is_my_network(ilist, &ni->addrs[i]) || IS_BROADCASTADDR(ni->addrs[i].s_addr))
{
ni_swap(ni, nnetwork, i);
nnetwork++;
}
}
_libinfo_ni_sys_interfaces_release(ilist);
stuff.ni = ni;
for (;;)
{
if (nlocal > 0)
{
for (i = 0; i < nlocal; i++)
{
syslog(LOG_DEBUG, "NetInfo connect call to: %s/%s (local %d)", inet_ntoa(ni->addrs[i]), ni->tags[i], i);
}
stat = multi_call(nlocal, ni->addrs, NIBIND_PROG, NIBIND_VERS, NIBIND_GETREGISTER, (xdrproc_t)xdr_ni_name, (void *)ni->tags, sizeof(ni_name), (xdrproc_t)xdr_nibind_getregister_res, (void *)&stuff, eachresult, NI_TIMEOUT_SHORT);
if (stat == RPC_SUCCESS) break;
}
if (nnetwork > nlocal)
{
for (i = 0; i < nnetwork; i++)
{
syslog(LOG_DEBUG, "NetInfo connect call to: %s/%s (network %d)", inet_ntoa(ni->addrs[i]), ni->tags[i], i);
}
stat = multi_call(nnetwork, ni->addrs, NIBIND_PROG, NIBIND_VERS, NIBIND_GETREGISTER, (xdrproc_t)xdr_ni_name, (void *)ni->tags, sizeof(ni_name), (xdrproc_t)xdr_nibind_getregister_res, (void *)&stuff, eachresult, NI_TIMEOUT_SHORT);
if (stat == RPC_SUCCESS) break;
}
for (i = 0; i < ni->naddrs; i++)
{
syslog(LOG_DEBUG, "NetInfo connect call to: %s/%s (world %d)", inet_ntoa(ni->addrs[i]), ni->tags[i], i);
}
stat = multi_call(ni->naddrs, ni->addrs, NIBIND_PROG, NIBIND_VERS, NIBIND_GETREGISTER, (xdrproc_t)xdr_ni_name, (void *)ni->tags, sizeof(ni_name), (xdrproc_t)xdr_nibind_getregister_res, (void *)&stuff, eachresult, (ni->rtv_sec == 0) ? NI_TIMEOUT_SHORT : ni->rtv_sec);
if (stat == RPC_SUCCESS) break;
if (ni->abort) return 0;
if (printed == 0)
{
if (ni->whichwrite >= 0)
{
syslog(LOG_WARNING,
"NetInfo connect timeout (domain with master %s/%s), sleeping", inet_ntoa(ni->addrs[ni->whichwrite]), ni->tags[ni->whichwrite]);
}
else
{
syslog(LOG_WARNING, "NetInfo connect timeout (domain with server %s/%s), sleeping", inet_ntoa(ni->addrs[0]), ni->tags[0]);
}
printed = 1;
}
sleep(sleeptime);
if (sleeptime < NI_MAXSLEEPTIME)
{
sleeptime *= 2;
}
}
syslog(LOG_INFO, "NetInfo connected to %s/%s", inet_ntoa(ni->addrs[0]), ni->tags[0]);
return 1;
}
static int
confirm_tcp(ni_private *ni, int needwrite)
{
int p;
if (ni->tsock != -1)
{
p = getmyport(ni->tsock);
if ((p != -1) && (p == ni->tport))
{
return 1;
}
if (p == -1)
{
close(ni->tsock);
}
if (ni->tc != NULL)
{
if (ni->tc->cl_auth != NULL) auth_destroy(ni->tc->cl_auth);
clnt_destroy(ni->tc);
ni->tc = NULL;
}
}
if ((needwrite == 0) && (rebind(ni) == 0) && (ni->abort != 0)) return 0;
return connectit(ni);
}
static int
setmaster(ni_private *ni)
{
ni_id root;
ni_namelist nl;
ni_name sep;
ni_idlist idl;
ni_name master;
ni_index i;
ni_index j;
ni_id id;
struct in_addr addr;
int needwrite;
if (ni->naddrs == 1)
{
ni->whichwrite = 0;
return 1;
}
needwrite = ni->needwrite;
ni->needwrite = 0;
if (ni_root(ni, &root) != NI_OK)
{
ni->needwrite = needwrite;
return 0;
}
NI_INIT(&nl);
if (ni_lookupprop(ni, &root, NAME_MASTER, &nl) != NI_OK)
{
ni->needwrite = needwrite;
return 0;
}
if (nl.ninl_len == 0)
{
ni->needwrite = needwrite;
return 0;
}
sep = index(nl.ninl_val[0], '/');
if (sep == NULL)
{
ni->needwrite = needwrite;
return 0;
}
*sep++ = 0;
master = nl.ninl_val[0];
NI_INIT(&idl);
if (ni_lookup(ni, &root, NAME_NAME, NAME_MACHINES, &idl) != NI_OK)
{
ni->needwrite = needwrite;
ni_namelist_free(&nl);
return 0;
}
if (idl.niil_len < 1)
{
ni->needwrite = needwrite;
return 0;
}
id.nii_object = idl.niil_val[0];
ni_idlist_free(&idl);
NI_INIT(&idl);
if (ni_lookup(ni, &id, NAME_NAME, master, &idl) != NI_OK)
{
ni_namelist_free(&nl);
ni->needwrite = needwrite;
return 0;
}
ni_namelist_free(&nl);
if (idl.niil_len < 1)
{
ni->needwrite = needwrite;
return 0;
}
id.nii_object = idl.niil_val[0];
ni_idlist_free(&idl);
NI_INIT(&nl);
if (ni_lookupprop(ni, &id, NAME_IP_ADDRESS, &nl) != NI_OK) return 0;
for (i = 0; i < nl.ninl_len; i++)
{
addr.s_addr = inet_addr(nl.ninl_val[i]);
for (j = 0; j < ni->naddrs; j++)
{
if (addr.s_addr == ni->addrs[j].s_addr)
{
ni->whichwrite = j;
ni_namelist_free(&nl);
ni->needwrite = needwrite;
return 1;
}
}
}
ni->needwrite = needwrite;
ni_namelist_free(&nl);
return 0;
}
static void *
callit(ni_private *ni, void *(*stub)(), void *args, int needwrite)
{
void *resp;
struct rpc_err err;
int i;
int sleeptime = 0;
int printed = 0;
if (getpid() != ni->pid) reinit(ni);
if (needwrite || ni->needwrite)
{
if (ni->whichwrite >= 0)
{
ni_switch(ni, ni->whichwrite);
}
else
{
if (setmaster(ni) == 0) return NULL;
ni_switch(ni, ni->whichwrite);
}
if (needwrite == 0)
{
ni_settimeout(ni, (ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec));
needwrite = 1;
}
else
{
ni_settimeout(ni, (ni->wtv_sec == 0 ? NI_TIMEOUT_LONG : ni->wtv_sec));
}
}
else
{
ni_settimeout(ni, (ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec));
}
for (;;)
{
for (i = 0; i < NI_MAXCONNTRIES; i++)
{
if (!confirm_tcp(ni, needwrite)) break;
if ((resp = (*stub)(args, ni->tc)) != NULL)
{
if (printed != 0) syslog(LOG_INFO, "NetInfo connected to %s/%s", inet_ntoa(ni->addrs[0]), ni->tags[0]);
return resp;
}
if (ni->tc != NULL)
{
clnt_geterr(ni->tc, &err);
if (err.re_status != RPC_CANTRECV) break;
}
if ((i + 1) < NI_MAXCONNTRIES)
{
reinit(ni);
}
}
if (err.re_status == RPC_PROCUNAVAIL) return NULL;
if (needwrite || ni->abort)
{
if (ni->abort) reinit(ni);
syslog(LOG_ERR, "NetInfo connection failed for server %s/%s", inet_ntoa(ni->addrs[0]), ni->tags[0]);
return NULL;
}
if (printed != 0)
{
if (ni->tc != NULL)
{
if ((sleeptime != 0) || (err.re_status != RPC_TIMEDOUT))
{
syslog(LOG_ERR, "%s on connection to %s/%s", clnt_sperror(ni->tc,"NetInfo connection timeout"), inet_ntoa(ni->addrs[0]), ni->tags[0]);
printed = 1;
}
else
{
syslog(LOG_NOTICE, "%s on initial connection to %s/%s", clnt_sperror(ni->tc,"NetInfo connection timeout"), inet_ntoa(ni->addrs[0]), ni->tags[0]);
}
}
else
{
syslog(LOG_ERR, "NetInfo connection failed for server %s/%s", inet_ntoa(ni->addrs[0]), ni->tags[0]);
printed = 1;
}
}
if (sleeptime > 0)
{
sleep(sleeptime);
if (sleeptime < NI_MAXSLEEPTIME) sleeptime *= 2;
}
else
{
sleeptime = NI_SLEEPTIME;
}
reinit(ni);
rebind(ni);
}
}
static void
ni_clear(ni_private *ni)
{
ni->needwrite = 0;
ni->naddrs = 0;
ni->addrs = NULL;
ni->tags = NULL;
ni->tc = NULL;
ni->tsock = -1;
ni->tport = -1;
ni->whichwrite = -1;
ni->passwd = NULL;
}
static void *
ni_alloc(void)
{
ni_private *ni;
ni = (ni_private *)malloc(sizeof(*ni));
ni->naddrs = 0;
ni->whichwrite = -1;
ni->pid = getpid();
ni->tsock = -1;
ni->tport = -1;
ni->tc = NULL;
ni->tv_sec = NI_TIMEOUT_SHORT;
ni->rtv_sec = 0;
ni->wtv_sec = 0;
ni->abort = 0;
ni->passwd = NULL;
ni->uid = getuid();
ni->needwrite = 0;
return (void *)ni;
}
void *
_ni_dup(void *ni)
{
ni_private *dupni;
ni_index i;
if (ni == NULL) return NULL;
dupni = (ni_private *)ni_alloc();
*dupni = *NIP(ni);
ni_clear(dupni);
dupni->naddrs = NIP(ni)->naddrs;
dupni->whichwrite = NIP(ni)->whichwrite;
if (dupni->naddrs > 0)
{
dupni->addrs = ((struct in_addr *) malloc(NIP(ni)->naddrs * sizeof(struct in_addr)));
bcopy(NIP(ni)->addrs, dupni->addrs, NIP(ni)->naddrs * sizeof(struct in_addr));
dupni->tags = ((ni_name *) malloc(NIP(ni)->naddrs * sizeof(ni_name)));
for (i = 0; i < NIP(ni)->naddrs; i++)
{
dupni->tags[i] = ni_name_dup(NIP(ni)->tags[i]);
}
}
if (NIP(ni)->passwd != NULL) dupni->passwd = ni_name_dup(NIP(ni)->passwd);
return (void *)dupni;
}
static int
match(ni_name domain, ni_name domtag, ni_name *tag)
{
int len = strlen(domain);
ni_name sep;
sep = index(domtag, '/');
if (sep == NULL) return 0;
if ((strncmp(domain, domtag, len) == 0) && (domtag[len] == '/'))
{
*tag = ni_name_dup(sep + 1);
return 1;
}
return 0;
}
static void
add_addr_tag(ni_private *ni, ni_name addrtag)
{
struct in_addr addr;
ni_name tag;
char *slash;
slash = strchr(addrtag, '/');
if (slash == NULL) return;
tag = slash + 1;
if (tag[0] == '\0') return;
*slash = '\0';
if (inet_aton(addrtag, &addr) == 0) return;
if (ni->naddrs == 0)
{
ni->addrs = (struct in_addr *)calloc(1, sizeof(struct in_addr));
if (ni->addrs == NULL) return;
ni->tags = (ni_name *)calloc(1, sizeof(ni_name));
if (ni->tags == NULL) return;
}
else
{
ni->addrs = (struct in_addr *)realloc(ni->addrs, ((ni->naddrs + 1) * sizeof(struct in_addr)));
if (ni->addrs == NULL) return;
ni->tags = (ni_name *)realloc(ni->tags, ((ni->naddrs + 1) * sizeof(ni_name)));
if (ni->tags == NULL) return;
}
ni->addrs[ni->naddrs] = addr;
ni->tags[ni->naddrs] = ni_name_dup(tag);
ni->naddrs++;
}
static int
addaddr(void *ni, ni_index ido, ni_name tag, ni_private *target_ni)
{
ni_id id;
ni_namelist nl;
struct in_addr addr;
int i;
ni_status status;
if (ni == NULL) return 0;
if (tag == NULL) return 0;
if (target_ni == NULL) return 0;
id.nii_object = ido;
NI_INIT(&nl);
status = ni_lookupprop(ni, &id, NAME_IP_ADDRESS, &nl);
if (status != NI_OK) return 0;
if (nl.ni_namelist_len == 0) return 0;
if (target_ni->naddrs == 0)
{
target_ni->addrs = (struct in_addr *)malloc(nl.ni_namelist_len * sizeof(struct in_addr));
target_ni->tags = (ni_name *)malloc(nl.ni_namelist_len * sizeof(ni_name));
}
else
{
target_ni->addrs = (struct in_addr *)realloc(target_ni->addrs, ((target_ni->naddrs + nl.ni_namelist_len) * sizeof(struct in_addr)));
target_ni->tags = (ni_name *)realloc(target_ni->tags, ((target_ni->naddrs + nl.ni_namelist_len) * sizeof(ni_name)));
}
for (i = 0; i < nl.ni_namelist_len; i++)
{
addr.s_addr = inet_addr(nl.ni_namelist_val[i]);
target_ni->addrs[target_ni->naddrs] = addr;
target_ni->tags[target_ni->naddrs] = ni_name_dup(tag);
target_ni->naddrs++;
}
ni_namelist_free(&nl);
return 1;
}
static int
get_daddr(ni_private *ni, ni_name dom, ni_private *target_ni)
{
ni_id nid;
ni_idlist ids;
ni_entrylist entries;
ni_proplist pl;
ni_index i;
ni_index j;
ni_name tag;
if (ni == NULL) return 0;
if (dom == NULL) return 0;
if (target_ni == NULL) return 0;
if (!strcmp(dom, "."))
{
NI_INIT(&pl);
if (ni_statistics(ni, &pl) == NI_OK)
{
i = ni_proplist_match(pl, NAME_DOMAIN_SERVERS, NULL);
if (i != NI_INDEX_NULL)
{
if (pl.ni_proplist_val[i].nip_val.ni_namelist_len > 0)
{
for (j = 0; j < pl.ni_proplist_val[i].nip_val.ni_namelist_len; j++)
{
add_addr_tag(target_ni, pl.ni_proplist_val[i].nip_val.ni_namelist_val[j]);
}
ni_proplist_free(&pl);
return 1;
}
}
ni_proplist_free(&pl);
}
}
if (ni_root(ni, &nid) != NI_OK) return 0;
NI_INIT(&ids);
if (ni_lookup(ni, &nid, NAME_NAME, NAME_MACHINES, &ids) != NI_OK) return 0;
nid.nii_object = ids.niil_val[0];
ni_idlist_free(&ids);
NI_INIT(&entries);
if (ni_list(ni, &nid, NAME_SERVES, &entries) != NI_OK) return 0;
for (i = 0; i < entries.niel_len; i++)
{
if (entries.niel_val[i].names == NULL) continue;
for (j = 0; j < entries.niel_val[i].names->ni_namelist_len; j++)
{
if (match(dom, entries.niel_val[i].names->ni_namelist_val[j], &tag))
{
addaddr(ni, entries.niel_val[i].id, tag, target_ni);
ni_name_free(&tag);
}
}
}
ni_entrylist_free(&entries);
return (target_ni->naddrs > 0);
}
static ni_status
getparent(ni_private *oldni, ni_private **newni)
{
ni_rparent_res *resp;
ni_private *ni = NULL;
ni_private *dupni;
int found = 0;
ni_index i;
struct in_addr raddr;
int printed = 0;
int inlist = 0;
if (oldni == NULL) return 0;
if (newni == NULL) return 0;
while (found == 0)
{
for (;;)
{
resp = RCALLIT(oldni, _ni_rparent_2, NULL);
if (resp == NULL) return NI_FAILED;
if (resp->status != NI_NORESPONSE) break;
if (printed != 0)
{
syslog(LOG_WARNING, "NetInfo timeout finding server for parent of %s/%s, sleeping", inet_ntoa(oldni->addrs[0]), oldni->tags[0]);
printed = 1;
}
sleep(NI_SLEEPTIME);
}
if (printed)
{
raddr.s_addr = htonl(resp->ni_rparent_res_u.binding.addr);
syslog(LOG_INFO, "NetInfo %s/%s found parent %s/%s", inet_ntoa(oldni->addrs[0]), oldni->tags[0], inet_ntoa(raddr), resp->ni_rparent_res_u.binding.tag);
}
if (resp->status != NI_OK) return resp->status;
ni = ni_alloc();
*ni = *oldni;
ni_clear(ni);
ni->naddrs = 1;
ni->addrs = (struct in_addr *)malloc(sizeof(struct in_addr));
ni->addrs[0].s_addr=htonl(resp->ni_rparent_res_u.binding.addr);
ni->tags = (ni_name *)malloc(sizeof(ni_name));
ni->tags[0] = ni_name_dup(resp->ni_rparent_res_u.binding.tag);
xdr_free((xdrproc_t)xdr_ni_rparent_res, (void *)resp);
dupni = ni;
ni = ni_alloc();
*ni = *dupni;
ni_clear(ni);
if (get_daddr(dupni, ".", ni) == 0)
{
if (oldni->abort == 1)
{
ni_free(dupni);
break;
}
}
else
{
for (i = 0; i < ni->naddrs; i++)
{
if (ni->addrs[i].s_addr == dupni->addrs[0].s_addr)
{
ni_switch(ni, i);
inlist++;
break;
}
}
ni->tsock = dupni->tsock;
ni->tport = dupni->tport;
ni->tc = dupni->tc;
dupni->tsock = -1;
dupni->tport = -1;
dupni->tc = NULL;
found = 1;
if (inlist == 0)
{
syslog(LOG_ERR, "Rogue NetInfo server detected: %s/%s", inet_ntoa(dupni->addrs[0]), dupni->tags[0]);
reinit(ni);
}
}
ni_free(dupni);
}
if (found)
{
*newni = ni;
return NI_OK;
}
if (ni != NULL) ni_free(ni);
return NI_FAILED;
}
void *
ni_connect(struct sockaddr_in *sin, const char *tag)
{
void *ni;
if (sin == NULL) return NULL;
if (tag == NULL) return NULL;
ni = ni_alloc();
NIP(ni)->naddrs = 1;
NIP(ni)->addrs = (struct in_addr *)malloc(sizeof(struct in_addr));
NIP(ni)->addrs[0] = sin->sin_addr;
NIP(ni)->tags = (ni_name *)malloc(sizeof(ni_name));
NIP(ni)->tags[0] = ni_name_dup(tag);
return ni;
}
ni_status
ni_addrtag(void *ni, struct sockaddr_in *addr, ni_name *tag)
{
if (ni == NULL) return NI_FAILED;
if (addr == NULL) return NI_FAILED;
if (tag == NULL) return NI_FAILED;
if (!confirm_tcp(ni, 0)) return NI_FAILED;
*tag = ni_name_dup(NIP(ni)->tags[0]);
addr->sin_addr = NIP(ni)->addrs[0];
addr->sin_port = htons(NIP(ni)->tport);
addr->sin_family = AF_INET;
bzero(addr->sin_zero, sizeof(addr->sin_zero));
return NI_OK;
}
void *
ni_new(void *oldni, const char *domain)
{
ni_private *ni;
ni_status status;
ni_name sep, addr, tag;
struct sockaddr_in sin;
struct hostent *he;
if (domain == NULL) return NULL;
sep = index(domain, '@');
if (sep != NULL)
{
tag = strncpy((char *)malloc(sep - domain + 1), domain, sep - domain);
tag[sep - domain] = '\0';
addr = NULL;
asprintf(&addr, "%s", sep + 1);
sin.sin_addr.s_addr = inet_addr(addr);
if (sin.sin_addr.s_addr == INADDR_NONE)
{
he = gethostbyname(addr);
if (he == NULL)
{
free(addr);
free(tag);
return NULL;
}
bcopy(he->h_addr_list[0], &sin.sin_addr.s_addr, he->h_length);
}
ni = ni_connect(&sin, tag);
free(addr);
free(tag);
return (void *)ni;
}
ni = NULL;
if (oldni == NULL)
{
ni = ni_alloc();
if (connectlocal(ni) == 0)
{
free(ni);
return NULL;
}
if (strcmp(domain, ".") == 0) return (void *)ni;
if (strcmp(domain, "..") == 0)
{
oldni = ni;
status = getparent((ni_private *)oldni, &ni);
ni_free(oldni);
if (status != NI_OK) return NULL;
return (void *)ni;
}
ni_free(ni);
return NULL;
}
if (strcmp(domain, "..") == 0)
{
status = getparent((ni_private *)oldni, &ni);
if (status != NI_OK) return NULL;
return (void *)ni;
}
ni = ni_alloc();
*ni = *NIP(oldni);
ni_clear(ni);
if (get_daddr(oldni, (ni_name)domain, ni) == 0)
{
ni_free(ni);
return NULL;
}
return (void *)ni;
}
void
ni_free(void *ni)
{
ni_index i;
if (ni == NULL) return;
if (NIP(ni)->tc != NULL) clnt_kill(NIP(ni)->tc, NIP(ni)->tsock, NIP(ni)->tport);
if (NIP(ni)->naddrs > 0)
{
for (i = 0; i < NIP(ni)->naddrs; i++) ni_name_free(&NIP(ni)->tags[i]);
free(NIP(ni)->tags);
free(NIP(ni)->addrs);
}
if (NIP(ni)->passwd != NULL) ni_name_free(&NIP(ni)->passwd);
free(ni);
}
ni_status
ni_statistics(void *ni, ni_proplist *pl)
{
ni_proplist *resp;
if (ni == NULL) return NI_FAILED;
if (pl == NULL) return NI_FAILED;
resp = (ni_proplist *)RCALLIT(ni, _ni_statistics_2, NULL);
if (resp == NULL)
{
clnt_debug(ni, "_ni_statistics");
return NI_FAILED;
}
*pl = *resp;
return NI_OK;
}
ni_status
ni_root(void *ni, ni_id *id)
{
ni_id_res *resp;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_root_2, id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_root");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_self(void *ni, ni_id *id)
{
ni_id_res *resp;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_self_2, id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_self");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_parent(void *ni, ni_id *id, ni_index *parent_id_p)
{
ni_parent_res *resp;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (parent_id_p == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_parent_2, id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_parent");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*parent_id_p = resp->ni_parent_res_u.stuff.object_id;
*id = resp->ni_parent_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_children(void *ni, ni_id *id, ni_idlist *children)
{
ni_children_res *resp;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (children == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_children_2, id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_children");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*children = resp->ni_children_res_u.stuff.children;
*id = resp->ni_children_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_create(void *ni, ni_id *parent_id, ni_proplist pl, ni_id *child_id_p, ni_index where)
{
ni_create_args args;
ni_create_res *resp;
if (ni == NULL) return NI_FAILED;
if (parent_id == NULL) return NI_FAILED;
if (child_id_p == NULL) return NI_FAILED;
args.id = *parent_id;
args.props = pl;
args.where = where;
args.target_id = NULL;
resp = WCALLIT(ni, _ni_create_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_create");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*child_id_p = resp->ni_create_res_u.stuff.id;
*parent_id = resp->ni_create_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_destroy(void *ni, ni_id *parent_id, ni_id self_id)
{
ni_id_res *resp;
ni_destroy_args args;
if (ni == NULL) return NI_FAILED;
if (parent_id == NULL) return NI_FAILED;
args.parent_id = *parent_id;
args.self_id = self_id;
resp = WCALLIT(ni, _ni_destroy_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_destroy");
return NI_FAILED;
}
if (resp->status == NI_OK) *parent_id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_write(void *ni, ni_id *self_id, ni_proplist pl)
{
ni_proplist_stuff args;
ni_id_res *resp;
if (ni == NULL) return NI_FAILED;
if (self_id == NULL) return NI_FAILED;
args.id = *self_id;
args.props = pl;
resp = WCALLIT(ni, _ni_write_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_write");
return NI_FAILED;
}
if (resp->status == NI_OK) *self_id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_read(void *ni, ni_id *self_id, ni_proplist *pl)
{
ni_proplist_res *resp;
if (ni == NULL) return NI_FAILED;
if (self_id == NULL) return NI_FAILED;
if (pl == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_read_2, self_id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_read");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*self_id = resp->ni_proplist_res_u.stuff.id;
*pl = resp->ni_proplist_res_u.stuff.props;
}
return resp->status;
}
ni_status
ni_lookup(void *ni, ni_id *id, ni_name_const pname, ni_name_const pval, ni_idlist *hits)
{
ni_lookup_res *resp;
ni_lookup_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (pname == NULL) return NI_FAILED;
if (hits == NULL) return NI_FAILED;
args.id = *id;
args.key = (ni_name)pname;
args.value = (ni_name)pval;
resp = RCALLIT(ni, _ni_lookup_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_lookup");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*hits = resp->ni_lookup_res_u.stuff.idlist;
*id = resp->ni_lookup_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_lookupread(void *ni, ni_id *id, ni_name_const pname, ni_name_const pval, ni_proplist *props)
{
ni_proplist_res *resp;
ni_lookup_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (pname == NULL) return NI_FAILED;
if (props == NULL) return NI_FAILED;
args.id = *id;
args.key = (ni_name)pname;
args.value = (ni_name)pval;
resp = RCALLIT(ni, _ni_lookupread_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_lookupread");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*props = resp->ni_proplist_res_u.stuff.props;
*id = resp->ni_proplist_res_u.stuff.id;
}
return resp->status;
}
ni_status
ni_list(void *ni, ni_id *id, ni_name_const pname, ni_entrylist *entries)
{
ni_list_res *resp;
ni_name_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (pname == NULL) return NI_FAILED;
if (entries == NULL) return NI_FAILED;
args.id = *id;
args.name = (ni_name)pname;
resp = RCALLIT(ni, _ni_list_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_list");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*entries = resp->ni_list_res_u.stuff.entries;
*id = resp->ni_list_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_listall(void *ni, ni_id *id, ni_proplist_list *entries)
{
ni_listall_res *resp;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (entries == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_listall_2, id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_listall");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*entries = resp->ni_listall_res_u.stuff.entries;
*id = resp->ni_listall_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_readprop(void *ni, ni_id *id, ni_index which, ni_namelist *propval_p)
{
ni_namelist_res *resp;
ni_prop_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (propval_p == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = which;
resp = RCALLIT(ni, _ni_readprop_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_readprop");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*propval_p = resp->ni_namelist_res_u.stuff.values;
*id = resp->ni_namelist_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_writeprop(void *ni, ni_id *id, ni_index which, ni_namelist propval)
{
ni_id_res *resp;
ni_writeprop_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = which;
args.values = propval;
resp = WCALLIT(ni, _ni_writeprop_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_writeprop");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_listprops(void *ni, ni_id *id, ni_namelist *propnames)
{
ni_namelist_res *resp;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (propnames == NULL) return NI_FAILED;
resp = RCALLIT(ni, _ni_listprops_2, id);
if (resp == NULL)
{
clnt_debug(ni, "_ni_listprops");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*propnames = resp->ni_namelist_res_u.stuff.values;
*id = resp->ni_namelist_res_u.stuff.self_id;
}
return resp->status;
}
ni_status
ni_createprop(void *ni, ni_id *id, ni_property prop, ni_index where)
{
ni_id_res *resp;
ni_createprop_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
args.id = *id;
args.prop = prop;
args.where = where;
resp = WCALLIT(ni, _ni_createprop_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_createprop");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_destroyprop(void *ni, ni_id *id, ni_index which)
{
ni_id_res *resp;
ni_prop_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = which;
resp = WCALLIT(ni, _ni_destroyprop_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_destroyprop");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_renameprop(void *ni, ni_id *id, ni_index prop_index, ni_name_const name)
{
ni_id_res *resp;
ni_propname_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (name == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = prop_index;
args.name = (ni_name)name;
resp = WCALLIT(ni, _ni_renameprop_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_renameprop");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_createname(void *ni, ni_id *id, ni_index prop_index, ni_name_const name, ni_index where)
{
ni_id_res *resp;
ni_createname_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (name == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = prop_index;
args.name = (ni_name)name;
args.where = where;
resp = WCALLIT(ni, _ni_createname_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_createname");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_destroyname(void *ni, ni_id *id, ni_index prop_index, ni_index name_index)
{
ni_id_res *resp;
ni_nameindex_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = prop_index;
args.name_index = name_index;
resp = WCALLIT(ni, _ni_destroyname_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_destroyname");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_writename(void *ni, ni_id *id, ni_index prop_index, ni_index name_index, ni_name_const name)
{
ni_id_res *resp;
ni_writename_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (name == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = prop_index;
args.name_index = name_index;
args.name = (ni_name)name;
resp = WCALLIT(ni, _ni_writename_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_writename");
return NI_FAILED;
}
if (resp->status == NI_OK) *id = resp->ni_id_res_u.id;
return resp->status;
}
ni_status
ni_readname(void *ni, ni_id *id, ni_index prop_index, ni_index name_index, ni_name *name)
{
ni_readname_res *resp;
ni_nameindex_args args;
if (ni == NULL) return NI_FAILED;
if (id == NULL) return NI_FAILED;
if (name == NULL) return NI_FAILED;
args.id = *id;
args.prop_index = prop_index;
args.name_index = name_index;
resp = RCALLIT(ni, _ni_readname_2, &args);
if (resp == NULL)
{
clnt_debug(ni, "_ni_readname");
return NI_FAILED;
}
if (resp->status == NI_OK)
{
*id = resp->ni_readname_res_u.stuff.id;
*name = resp->ni_readname_res_u.stuff.name;
}
return resp->status;
}
ni_status
ni_resync(void *ni)
{
ni_status *resp;
if (ni == NULL) return NI_FAILED;
resp = (ni_status *)RCALLIT(ni, _ni_resync_2, NULL);
if (resp == NULL)
{
clnt_debug(ni, "_ni_resync");
return NI_FAILED;
}
return *resp;
}
ni_status
ni_setuser(void *ni, ni_name_const user)
{
ni_id id;
ni_idlist ids;
ni_namelist nl;
char *p;
ni_status status;
if (ni == NULL) return NI_FAILED;
if (user == NULL)
{
NIP(ni)->uid = getuid();
return ni_setpassword(ni, NULL);
}
status = ni_root(ni, &id);
if (status != NI_OK) return NI_NOUSER;
NI_INIT(&ids);
status = ni_lookup(ni, &id, NAME_NAME, NAME_USERS, &ids);
if (status != NI_OK) return NI_NOUSER;
id.nii_object = ids.niil_val[0];
ni_idlist_free(&ids);
NI_INIT(&ids);
status = ni_lookup(ni, &id, NAME_NAME, user, &ids);
if (status != NI_OK) return NI_NOUSER;
id.nii_object = ids.niil_val[0];
ni_idlist_free(&ids);
NI_INIT(&nl);
status = ni_lookupprop(ni, &id, NAME_UID, &nl);
if (status != NI_OK) return NI_NOUSER;
if (nl.ninl_len == 0) return NI_NOUSER;
for (p = nl.ninl_val[0]; *p; p++)
{
if (isdigit(*p) == 0)
{
ni_namelist_free(&nl);
return NI_NOUSER;
}
}
NIP(ni)->uid = atoi(nl.ninl_val[0]);
if (NIP(ni)->passwd == NULL) NIP(ni)->passwd = ni_name_dup("");
createauth(NIP(ni));
return NI_OK;
}
ni_status
ni_setpassword(void *ni, ni_name_const passwd)
{
char *p;
if (ni == NULL) return NI_FAILED;
if (NIP(ni)->passwd != NULL) ni_name_free(&NIP(ni)->passwd);
if (passwd == NULL)
{
NIP(ni)->passwd = NULL;
if (NIP(ni)->tc != NULL)
{
auth_destroy(NIP(ni)->tc->cl_auth);
NIP(ni)->tc->cl_auth = authnone_create();
}
return NI_OK;
}
NIP(ni)->passwd = ni_name_dup(passwd);
for (p = NIP(ni)->passwd; *p; p++) *p = ~(*p);
createauth(NIP(ni));
return NI_OK;
}
static u_short
pmap_getport_to(address, program, version, protocol, timeout_secs, ntries)
struct sockaddr_in *address;
u_long program;
u_long version;
u_int protocol;
int timeout_secs;
int ntries;
{
u_short port = 0;
int sock = -1;
register CLIENT *client;
struct pmap parms;
struct timeval timeout;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) return 0;
address->sin_port = htons(PMAPPORT);
timeout.tv_usec = ((timeout_secs % ntries) * 1000000) / ntries;
timeout.tv_sec = (timeout_secs / ntries);
client = clntudp_bufcreate(address, PMAPPROG, PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
if (client != NULL)
{
parms.pm_prog = program;
parms.pm_vers = version;
parms.pm_prot = protocol;
parms.pm_port = 0;
timeout.tv_usec = 0;
timeout.tv_sec = timeout_secs;
if (CLNT_CALL(client, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap, (char *)&parms, (xdrproc_t)xdr_u_short, (char *)&port, timeout) != RPC_SUCCESS)
{
rpc_createerr.cf_stat = RPC_PMAPFAILURE;
clnt_geterr(client, &rpc_createerr.cf_error);
port = 0;
}
else if (port == 0)
{
rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
}
}
if (client != NULL) clnt_destroy(client);
close(sock);
address->sin_port = 0;
return port;
}
static int
socket_open(struct sockaddr_in *raddr, int prog, int vers, int timeout, int ntries, int proto)
{
struct sockaddr_in bindsin;
int sock;
int reuse = 1;
u_short port;
memset(&bindsin, 0, sizeof(bindsin));
if (raddr->sin_port == 0)
{
port = pmap_getport_to(raddr, prog, vers, IPPROTO_UDP, timeout, ntries);
if (port == 0) return -1;
raddr->sin_port = htons(port);
}
sock = socket(AF_INET, proto == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM, proto);
if (sock < 0) return -1;
if ((bindresvport(sock, NULL) < 0) && (errno == EADDRNOTAVAIL))
{
syslog(LOG_DEBUG, "Libinfo[%s():%d] bindresvport(): %m", __func__, __LINE__);
if (bind(sock, (struct sockaddr *)&bindsin, sizeof(bindsin)) < 0)
{
syslog(LOG_DEBUG, "Libinfo[%s():%d] bind(): %m", __func__, __LINE__);
close(sock);
return -1;
}
}
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if (proto == IPPROTO_TCP)
{
if (connect(sock, (struct sockaddr *)raddr, sizeof(*raddr)) < 0)
{
syslog(LOG_DEBUG, "Libinfo[%s():%d] connect(): %m", __func__, __LINE__);
close(sock);
return -1;
}
}
return sock;
}