#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
#include <netinfo/ni.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>
extern char *inet_ntoa();
static const char NAME_NAME[] = "name";
static const char NAME_MACHINES[] = "machines";
static const char NAME_IP_ADDRESS[] = "ip_address";
static const char NAME_SERVES[] = "serves";
static const char NAME_UNKNOWN[] = "###UNKNOWN###";
#define NI_READ_TIMEOUT 10
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))
static ni_status
my_rparent(void *domain, struct sockaddr_in *addr, char **tag)
{
CLIENT *c;
ni_rparent_res *rpres;
if (domain == NULL) return NI_INVALIDDOMAIN;
if (addr == NULL) return NI_NONAME;
if (tag == NULL) return NI_NONAME;
c = NIP(domain)->tc;
if (c == NULL) return NI_SYSTEMERR;
rpres = _ni_rparent_2((void *)0, c);
if (rpres == NULL) return NI_SYSTEMERR;
if (rpres->status != NI_OK) return rpres->status;
addr->sin_addr.s_addr = htonl(rpres->ni_rparent_res_u.binding.addr);
*tag = malloc(strlen(rpres->ni_rparent_res_u.binding.tag) + 1);
strcpy(*tag, rpres->ni_rparent_res_u.binding.tag);
free(rpres->ni_rparent_res_u.binding.tag);
return NI_OK;
}
static void *
_ni_connect_parent(void *ni)
{
struct sockaddr_in addr;
char *tag;
ni_status status;
void *p;
ni_id nid;
if (ni == NULL) return NULL;
memset(&addr, 0, sizeof(struct sockaddr_in));
tag = NULL;
status = my_rparent(ni, &addr, &tag);
if (status != NI_OK) return NULL;
p = ni_connect(&addr, tag);
if (tag != NULL) free(tag);
nid.nii_object = 0;
nid.nii_instance = 0;
ni_setreadtimeout(p, NI_READ_TIMEOUT);
ni_setabort(ni, 1);
status = ni_self(p, &nid);
if (status != NI_OK)
{
ni_free(p);
return NULL;
}
return p;
}
static ni_name
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 *
finddomain(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_NAME, 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, NAME_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, NAME_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 = escape_domain(nl.ninl_val[i]);
ni_namelist_free(&nl);
return domain;
}
}
ni_namelist_free(&nl);
return NULL;
}
static int
sys_is_my_address(struct in_addr *a)
{
struct ifaddrs *ifa, *p;
if (getifaddrs(&ifa) < 0) 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)
{
freeifaddrs(ifa);
return 1;
}
}
freeifaddrs(ifa);
return 0;
}
static char *
ni_domainof(void *ni, void *parent)
{
struct sockaddr_in addr;
ni_name tag;
ni_name dom;
ni_status status;
struct ifaddrs *ifa, *p;
status = ni_addrtag(ni, &addr, &tag);
if (status != NI_OK) return ni_name_dup(NAME_UNKNOWN);
dom = finddomain(parent, addr.sin_addr, tag);
if (dom != NULL)
{
ni_name_free(&tag);
return dom;
}
if (getifaddrs(&ifa) < 0) return ni_name_dup(NAME_UNKNOWN);
if (sys_is_my_address(&(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 = finddomain(parent, addr.sin_addr, tag);
if (dom != NULL)
{
ni_name_free(&tag);
freeifaddrs(ifa);
return dom;
}
}
}
freeifaddrs(ifa);
dom = malloc(strlen(tag) + 256);
sprintf(dom, "%s@%s", tag, inet_ntoa(addr.sin_addr));
ni_name_free(&tag);
return dom;
}
static ni_status
_ni_pwdomain(void *ni, ni_name *buf)
{
void *nip;
ni_status status;
int len;
char *dom;
nip = _ni_connect_parent(ni);
if (nip == NULL)
{
(*buf) = malloc(2);
(*buf)[0] = 0;
return NI_OK;
}
status = _ni_pwdomain(nip, buf);
if (status != NI_OK) return status;
dom = ni_domainof(ni, nip);
len = strlen(*buf);
*buf = realloc(*buf, len + 1 + strlen(dom) + 1);
(*buf)[len] = '/';
strcpy(&(*buf)[len + 1], dom);
ni_name_free(&dom);
ni_free(nip);
return NI_OK;
}
ni_status
my_ni_pwdomain(void *ni, ni_name *buf)
{
ni_status status;
*buf = NULL;
status = _ni_pwdomain(ni, buf);
if (status != NI_OK)
{
if (*buf != NULL) ni_name_free(buf);
return status;
}
if ((*buf)[0] == 0)
{
(*buf)[0] = '/';
(*buf)[1] = 0;
}
return NI_OK;
}