#include <netinfo/ni.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/clnt.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <notify.h>
#include "netinfo_open.h"
#define LOCAL_PORT 1033
#define NAME_UNKNOWN "###UNKNOWN###"
#define NUM_RETRY 4
#define SHARED_FLAGS_ISROOT 0x00000001
#define SHARED_ISROOT_TIMEOUT 300
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;
typedef struct shared_ref_s
{
void *domain;
char *relname;
uint32_t flags;
time_t timeout;
uint32_t isroot_time;
struct shared_ref_s *parent;
} shared_ref_t;
static int shared_ref_count = 0;
static shared_ref_t *shared_ref_local = NULL;
static shared_ref_t **shared_ref = NULL;
static int shared_ref_local_token = -1;
static shared_ref_t *
_shared_ni_new_ref(void *domain, int timeout)
{
shared_ref_t *a;
if (domain == NULL) return NULL;
if (shared_ref_count == 0) shared_ref = (shared_ref_t **)calloc(1, sizeof(shared_ref_t *));
else shared_ref = (shared_ref_t **)realloc(shared_ref, (shared_ref_count + 1) * sizeof(shared_ref_t *));
if (shared_ref == NULL) return NULL;
a = (shared_ref_t *)calloc(1, sizeof(shared_ref_t));
if (a == NULL) return NULL;
a->domain = domain;
a->timeout = timeout;
shared_ref[shared_ref_count] = a;
shared_ref_count++;
return a;
}
static ni_status
_shared_ni_ping(unsigned short port, struct in_addr addr, int t)
{
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 = t;
timeout.tv_usec = 0;
retry.tv_sec = t / NUM_RETRY;
retry.tv_usec = (t - (NUM_RETRY * retry.tv_sec)) * (1000000 / NUM_RETRY);
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 int
_shared_ni_match(shared_ref_t *s, struct in_addr *a, const char *t)
{
ni_private *ni;
unsigned long i;
if (s == NULL) return 0;
if (s->domain == NULL) return 0;
if (a == NULL) return 0;
if (t == NULL) return 0;
ni = (ni_private *)s->domain;
if (ni == NULL) return 0;
for (i = 0; i < ni->naddrs; i++)
{
if ((ni->addrs[i].s_addr == a->s_addr) && (strcmp(ni->tags[i], t) == 0)) return 1;
}
return 0;
}
static shared_ref_t *
_shared_ni_find(struct in_addr *addr, const char *tag)
{
int i;
for (i = 0; i < shared_ref_count; i++)
{
if (_shared_ni_match(shared_ref[i], addr, tag)) return shared_ref[i];
}
return NULL;
}
static shared_ref_t *
_shared_ni_find_ni(void *ni)
{
int i;
if (ni == NULL) return NULL;
for (i = 0; i < shared_ref_count; i++)
{
if (shared_ref[i]->domain == ni) return shared_ref[i];
}
return NULL;
}
static void
_shared_ni_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
_shared_ni_add_addr(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, "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 ni_private *
_shared_ni_alloc(int timeout)
{
ni_private *ni;
ni = (ni_private *)calloc(1, sizeof(*ni));
ni->whichwrite = -1;
ni->pid = getpid();
ni->tsock = -1;
ni->tport = -1;
ni->rtv_sec = timeout;
ni->wtv_sec = timeout;
ni->abort = 1;
ni->uid = getuid();
return ni;
}
static int
_shared_ni_serves_match(const char *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 ni_private *
_shared_ni_csopen(void *ni, const char *name, int timeout)
{
ni_id nid;
ni_idlist ids;
ni_entrylist entries;
ni_proplist pl;
ni_index i;
ni_index j;
ni_name tag;
ni_private *nip;
ni_status status;
if (ni == NULL) return NULL;
if (name == NULL) return NULL;
if (!strcmp(name, "..")) return NULL;
nip = _shared_ni_alloc(timeout);
ni_setreadtimeout(ni, timeout);
ni_setwritetimeout(ni, timeout);
ni_setabort(ni, 1);
if (!strcmp(name, "."))
{
NI_INIT(&pl);
if (ni_statistics(ni, &pl) == NI_OK)
{
i = ni_proplist_match(pl, "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++)
{
_shared_ni_add_addr_tag(nip, pl.ni_proplist_val[i].nip_val.ni_namelist_val[j]);
}
ni_proplist_free(&pl);
return nip;
}
}
ni_proplist_free(&pl);
}
}
if (ni_root(ni, &nid) != NI_OK)
{
free(nip);
return NULL;
}
NI_INIT(&ids);
if (ni_lookup(ni, &nid, "name", "machines", &ids) != NI_OK)
{
free(nip);
return NULL;
}
nid.nii_object = ids.niil_val[0];
ni_idlist_free(&ids);
NI_INIT(&entries);
if (ni_list(ni, &nid, "serves", &entries) != NI_OK)
{
free(nip);
return NULL;
}
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 (_shared_ni_serves_match(name, entries.niel_val[i].names->ni_namelist_val[j], &tag))
{
_shared_ni_add_addr(ni, entries.niel_val[i].id, tag, nip);
ni_name_free(&tag);
}
}
}
ni_entrylist_free(&entries);
if (nip->naddrs == 0)
{
free(nip);
return NULL;
}
nid.nii_object = 0;
nid.nii_instance = 0;
status = ni_self(nip, &nid);
if (status != NI_OK)
{
ni_free(nip);
return NULL;
}
return nip;
}
static shared_ref_t *
_shared_ni_connection(struct in_addr *addr, const char *tag, int timeout)
{
struct sockaddr_in sa;
void *d, *tmp;
ni_status status;
ni_id root;
shared_ref_t *s;
if (addr == NULL) return NULL;
if (tag == NULL) return NULL;
s = _shared_ni_find(addr, tag);
if (s != NULL) return s;
memset(&sa, 0, sizeof(struct in_addr));
sa.sin_family = AF_INET;
sa.sin_addr = *addr;
d = ni_connect(&sa, tag);
if (d == NULL) return NULL;
ni_setreadtimeout(d, timeout);
ni_setwritetimeout(d, timeout);
ni_setabort(d, 1);
if (strcmp(tag, "local") != 0)
{
tmp = _shared_ni_csopen(d, ".", timeout);
ni_free(d);
d = tmp;
if (d == NULL) return NULL;
ni_setreadtimeout(d, timeout);
ni_setwritetimeout(d, timeout);
ni_setabort(d, 1);
}
root.nii_object = 0;
root.nii_instance = 0;
status = ni_self(d, &root);
if (status != NI_OK)
{
ni_free(d);
return NULL;
}
s = _shared_ni_new_ref(d, timeout);
return s;
}
static shared_ref_t *
_shared_ni_local(int timeout)
{
struct sockaddr_in sin;
int sock, status;
void *d;
ni_id root;
if (shared_ref_local != NULL) return shared_ref_local;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(LOCAL_PORT);
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
status = connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
close(sock);
if (status < 0) return NULL;
status = _shared_ni_ping(sin.sin_port, sin.sin_addr, timeout);
if (status != NI_OK) return NULL;
d = ni_connect(&sin, "local");
ni_setreadtimeout(d, timeout);
ni_setwritetimeout(d, timeout);
ni_setabort(d, 1);
root.nii_object = 0;
root.nii_instance = 0;
status = ni_self(d, &root);
if (status != NI_OK)
{
ni_free(d);
return NULL;
}
shared_ref_local = _shared_ni_new_ref(d, timeout);
status = notify_register_plain(NETINFO_BINDING_KEY, &shared_ref_local_token);
if (status != NOTIFY_STATUS_OK) shared_ref_local_token = -1;
return shared_ref_local;
}
static shared_ref_t *
_shared_ni_parent(shared_ref_t *s, int timeout)
{
ni_rparent_res rpres;
ni_private *ni;
struct in_addr addr;
shared_ref_t *p;
struct timeval tnew, tcurr;
time_t now;
enum clnt_stat rpc_status;
ni_status status;
int nstatus, binding;
if (s == NULL) return NULL;
if (s->parent != NULL) return s->parent;
now = 0;
if ((s == shared_ref_local) && (s->flags & SHARED_FLAGS_ISROOT))
{
if (shared_ref_local_token == -1)
{
nstatus = notify_register_plain(NETINFO_BINDING_KEY, &shared_ref_local_token);
if (nstatus != NOTIFY_STATUS_OK) shared_ref_local_token = -1;
}
if (shared_ref_local_token != -1)
{
nstatus = notify_get_state(shared_ref_local_token, &binding);
if ((nstatus == NOTIFY_STATUS_OK) && (binding != BINDING_STATE_NETROOT))
{
s->isroot_time = 0;
s->flags &= ~SHARED_FLAGS_ISROOT;
}
}
}
if (s->flags & SHARED_FLAGS_ISROOT)
{
now = time(NULL);
if (now <= (s->isroot_time + SHARED_ISROOT_TIMEOUT)) return NULL;
s->flags &= ~SHARED_FLAGS_ISROOT;
s->isroot_time = 0;
}
ni = (ni_private *)s->domain;
status = NI_FAILED;
if (ni->tc != NULL)
{
memset(&rpres, 0, sizeof(ni_rparent_res));
tnew.tv_sec = timeout;
tnew.tv_usec = 0;
tcurr.tv_sec = 0;
tcurr.tv_usec = 0;
clnt_control(ni->tc, CLGET_TIMEOUT, (void *)&tcurr);
clnt_control(ni->tc, CLSET_TIMEOUT, (void *)&tnew);
rpc_status = clnt_call(ni->tc, _NI_RPARENT, (void *)xdr_void, NULL, (void *)xdr_ni_rparent_res, &rpres, tnew);
clnt_control(ni->tc, CLSET_TIMEOUT, (void *)&tcurr);
if (rpc_status == RPC_SUCCESS) status = rpres.status;
}
if (status == NI_NETROOT)
{
if (now == 0) now = time(NULL);
s->isroot_time = now;
s->flags |= SHARED_FLAGS_ISROOT;
return NULL;
}
if (status != NI_OK) return NULL;
addr.s_addr = htonl(rpres.ni_rparent_res_u.binding.addr);
p = _shared_ni_connection(&addr, rpres.ni_rparent_res_u.binding.tag, timeout);
free(rpres.ni_rparent_res_u.binding.tag);
s->parent = p;
return p;
}
static shared_ref_t *
_shared_ni_relopen(shared_ref_t *s, const char *name, int timeout)
{
ni_private *ni;
ni_id root;
shared_ref_t *x;
int i;
ni_status status;
if (s == NULL) return NULL;
if (!strcmp(name, ".")) return s;
if (!strcmp(name, "..")) return _shared_ni_parent(s, timeout);
if (!strcmp(name, "/"))
{
for (;;)
{
x = _shared_ni_parent(s, timeout);
if (x == NULL) return s;
s = x;
}
}
for (i = 0; i < shared_ref_count; i++)
{
if (shared_ref[i] == NULL) continue;
if (shared_ref[i]->relname == NULL) continue;
if ((shared_ref[i]->parent == s) && (!strcmp(shared_ref[i]->relname, name))) return shared_ref[i];
}
ni = _shared_ni_csopen(s->domain, name, timeout);
if (ni == NULL)
return NULL;
x = _shared_ni_find(&(ni->addrs[0]), ni->tags[0]);
if (x != NULL)
{
ni_free(ni);
if (x->parent == NULL) x->parent = s;
if (x->relname == NULL) x->relname = strdup(name);
return x;
}
root.nii_object = 0;
root.nii_instance = 0;
status = ni_self(ni, &root);
if (status != NI_OK)
{
ni_free(ni);
return NULL;
}
x = _shared_ni_new_ref(ni, timeout);
if (x == NULL)
{
ni_free(ni);
return NULL;
}
x->parent = s;
x->relname = strdup(name);
return x;
}
static ni_name
_shared_ni_escape_domain(ni_name name)
{
int extra;
char *p;
char *s;
ni_name newname;
extra = 0;
for (p = name; *p; p++)
{
if ((*p == '/') || (*p == '\\')) extra++;
}
newname = malloc(strlen(name) + extra + 1);
s = newname;
for (p = name; *p; p++)
{
if ((*p == '/') || (*p == '\\')) *s++ = '\\';
*s++ = *p;
}
*s = 0;
return newname;
}
static char *
_shared_ni_find_domain(void *ni, struct in_addr addr, ni_name tag)
{
ni_id nid;
ni_idlist idl;
ni_namelist nl;
ni_index i;
ni_name slash;
ni_name domain;
ni_status status;
status = ni_root(ni, &nid);
if (status != NI_OK) return NULL;
status = ni_lookup(ni, &nid, "name", "machines", &idl);
if (status != NI_OK) return NULL;
nid.nii_object = idl.niil_val[0];
ni_idlist_free(&idl);
status = ni_lookup(ni, &nid, "ip_address", inet_ntoa(addr), &idl);
if (status != NI_OK) return NULL;
nid.nii_object = idl.niil_val[0];
ni_idlist_free(&idl);
status = ni_lookupprop(ni, &nid, "serves", &nl);
if (status != NI_OK) return NULL;
for (i = 0; i < nl.ninl_len; i++)
{
slash = rindex(nl.ninl_val[i], '/');
if (slash == NULL) continue;
if (ni_name_match(slash + 1, tag))
{
*slash = 0;
domain = _shared_ni_escape_domain(nl.ninl_val[i]);
ni_namelist_free(&nl);
return domain;
}
}
ni_namelist_free(&nl);
return NULL;
}
static int
_shared_ni_is_my_address(struct ifaddrs *ifa, struct in_addr *a)
{
struct ifaddrs *p;
if (ifa == NULL) return 0;
if (a == NULL) return 0;
for (p = ifa; p != NULL; p = p->ifa_next)
{
if (p->ifa_addr == NULL) continue;
if ((p->ifa_flags & IFF_UP) == 0) continue;
if (p->ifa_addr->sa_family != AF_INET) continue;
if (a->s_addr == ((struct sockaddr_in *)(p->ifa_addr))->sin_addr.s_addr) return 1;
}
return 0;
}
static char *
_shared_ni_domainof(shared_ref_t *ni, shared_ref_t *parent)
{
struct sockaddr_in addr;
char *dom;
struct ifaddrs *ifa, *p;
ni_private *nip;
if (ni == NULL) return ni_name_dup(NAME_UNKNOWN);
if (parent == NULL) return ni_name_dup(NAME_UNKNOWN);
nip = (ni_private *)ni->domain;
addr.sin_addr = nip->addrs[0];
dom = _shared_ni_find_domain(parent->domain, nip->addrs[0], nip->tags[0]);
if (dom != NULL) return dom;
if (getifaddrs(&ifa) < 0) return ni_name_dup(NAME_UNKNOWN);
if (_shared_ni_is_my_address(ifa, &(addr.sin_addr)))
{
for (p = ifa; p != NULL; p = p->ifa_next)
{
if (p->ifa_addr == NULL) continue;
if ((p->ifa_flags & IFF_UP) == 0) continue;
if (p->ifa_addr->sa_family != AF_INET) continue;
addr.sin_addr.s_addr = ((struct sockaddr_in *)(p->ifa_addr))->sin_addr.s_addr;
if (addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) continue;
dom = _shared_ni_find_domain(parent->domain, addr.sin_addr, nip->tags[0]);
if (dom != NULL)
{
freeifaddrs(ifa);
return dom;
}
}
}
freeifaddrs(ifa);
asprintf(&dom, "%s@%s", nip->tags[0], inet_ntoa(addr.sin_addr));
return dom;
}
static const char *
_shared_ni_reldomainname(shared_ref_t *s)
{
shared_ref_t *p;
if (s == NULL) return NULL;
if (s->relname != NULL) return (const char *)s->relname;
p = _shared_ni_parent(s, s->timeout);
if (p == NULL)
{
return "/";
}
s->relname = _shared_ni_domainof(s, p);
return (const char *)s->relname;
}
static char *
_shared_ni_domainname(shared_ref_t *d)
{
char *s, *t;
const char *name;
shared_ref_t *p;
if (d == NULL) return strdup(NAME_UNKNOWN);
s = NULL;
for (p = d; p != NULL; p = _shared_ni_parent(p, p->timeout))
{
name = _shared_ni_reldomainname(p);
if (name == NULL) name = NAME_UNKNOWN;
if (!strcmp(name, "/"))
{
if (s == NULL) asprintf(&s, "/");
return s;
}
if (s == NULL)
{
asprintf(&s, "/%s", name);
}
else
{
asprintf(&t, "/%s%s", name, s);
free(s);
s = t;
}
}
return s;
}
#ifdef DEBUG
static void
_shared_ni_print(FILE *f)
{
int i, delta;
time_t now;
fprintf(f, "shared_ref_count = %d\n", shared_ref_count);
if (shared_ref_count == 0) return;
now = time(NULL);
fprintf(f, " self ni parent name\n");
for (i = 0; i < shared_ref_count; i++)
{
fprintf(f, "%2d: 0x%08x 0x%08x 0x%08x", i, (unsigned int)shared_ref[i], (unsigned int)shared_ref[i]->domain, (unsigned int)shared_ref[i]->parent);
if (shared_ref[i]->flags & SHARED_FLAGS_ISROOT)
{
delta = now - shared_ref[i]->isroot_time;
fprintf(f, " / (%d)", delta);
}
else if (shared_ref[i]->relname != NULL) fprintf(f, " %s", shared_ref[i]->relname);
fprintf(f, "\n");
}
}
#endif
void
netinfo_clear(int flags)
{
int i;
for (i = 0; i < shared_ref_count; i++)
{
if (shared_ref[i] == shared_ref_local)
{
if (flags | NETINFO_CLEAR_PRESERVE_LOCAL) continue;
else shared_ref_local = NULL;
}
if (shared_ref[i]->domain != NULL) ni_free(shared_ref[i]->domain);
if (shared_ref[i]->relname != NULL) free(shared_ref[i]->relname);
}
if (shared_ref != NULL) free(shared_ref);
shared_ref = NULL;
shared_ref_count = 0;
if (shared_ref_local != NULL)
{
if (shared_ref_local->relname != NULL) free(shared_ref_local->relname);
shared_ref_local->relname = NULL;
shared_ref_local->flags = 0;
shared_ref_local->isroot_time = 0;
shared_ref_local->parent = NULL;
shared_ref = (shared_ref_t **)calloc(1, sizeof(shared_ref_t *));
shared_ref[0] = shared_ref_local;
shared_ref_count = 1;
}
}
ni_status
netinfo_local(void **domain, int timeout)
{
shared_ref_t *s;
if (domain == NULL) return NI_INVALIDDOMAIN;
*domain = NULL;
s = _shared_ni_local(timeout);
if (s == NULL) return NI_FAILED;
*domain = s->domain;
return NI_OK;
}
ni_status
netinfo_connect(struct in_addr *addr, const char *tag, void **ni, int timeout)
{
shared_ref_t *s;
if (addr == NULL) return NI_FAILED;
if (tag == NULL) return NI_FAILED;
if (ni == NULL) return NI_FAILED;
*ni = NULL;
s = _shared_ni_connection(addr, tag, timeout);
if (s == NULL) return NI_FAILED;
*ni = s->domain;
return NI_OK;
}
ni_status
netinfo_open(void *ni, char *name, void **newni, int timeout)
{
shared_ref_t *s;
ni_private *p;
char *cname, *x, *slash;
if (name == NULL) return NI_FAILED;
if (newni == NULL) return NI_FAILED;
s = NULL;
if (ni == NULL)
{
if (!strcmp(name, ".")) return netinfo_local(newni, timeout);
s = _shared_ni_local(timeout);
}
else
{
p = (ni_private *)ni;
s = _shared_ni_connection(&(p->addrs[0]), p->tags[0], timeout);
}
if (s == NULL) return NI_FAILED;
cname = strdup(name);
x = cname;
if (x[0] == '/')
{
s = _shared_ni_relopen(s, "/", timeout);
x++;
}
if (x[0] == '\0') x = NULL;
while (x != NULL)
{
slash = strchr(x, '/');
if (slash != NULL) *slash = '\0';
s = _shared_ni_relopen(s, x, timeout);
if (s == NULL)
{
free(cname);
return NI_FAILED;
}
if (slash == NULL) x = NULL;
else
{
if (x[0] == '\0') x = NULL;
x = slash+1;
}
}
free(cname);
if (s == NULL) return NI_FAILED;
*newni = s->domain;
return NI_OK;
}
char *
netinfo_domainname(void *ni)
{
shared_ref_t *s;
s = _shared_ni_find_ni(ni);
return _shared_ni_domainname(s);
}