#include <stdlib.h>
#include <mach/mach.h>
#include <stdio.h>
#include <string.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <netinet/if_ether.h>
#include "lu_host.h"
#define ENTRY_SIZE sizeof(struct hostent)
#define ENTRY_KEY _li_data_key_host
#define HOST_CACHE_SIZE 20
#define CACHE_BYNAME 0
#define CACHE_BYADDR 1
static pthread_mutex_t _host_cache_lock = PTHREAD_MUTEX_INITIALIZER;
static void *_host_byname_cache[HOST_CACHE_SIZE] = { NULL };
static int _host_byname_cache_flavor[HOST_CACHE_SIZE] = { WANT_NOTHING };
static unsigned int _host_byname_cache_index = 0;
static void *_host_byaddr_cache[HOST_CACHE_SIZE] = { NULL };
static int _host_byaddr_cache_flavor[HOST_CACHE_SIZE] = { WANT_NOTHING };
static unsigned int _host_byaddr_cache_index = 0;
static unsigned int _host_cache_init = 0;
static pthread_mutex_t _host_lock = PTHREAD_MUTEX_INITIALIZER;
__private_extern__ struct hostent *LI_files_gethostbyname(const char *name);
__private_extern__ struct hostent *LI_files_gethostbyname2(const char *name, int af);
__private_extern__ struct hostent *LI_files_gethostbyaddr(const void *addr, socklen_t len, int type);
__private_extern__ struct hostent *LI_files_gethostent();
__private_extern__ void LI_files_sethostent(int stayopen);
__private_extern__ void LI_files_endhostent();
extern int _old_ether_hostton(const char *, struct ether_addr *);
extern int _old_ether_ntohost(char *, const struct ether_addr *);
extern int h_errno;
#define IPV6_ADDR_LEN 16
#define IPV4_ADDR_LEN 4
void
freehostent(struct hostent *h)
{
char **aliases;
int i;
if (LI_ils_free(h, ENTRY_SIZE) == 0) return;
if (h->h_name != NULL) free(h->h_name);
aliases = h->h_aliases;
if (aliases != NULL)
{
while (*aliases != NULL) free(*aliases++);
free(h->h_aliases);
}
if (h->h_addr_list != NULL)
{
for (i = 0; h->h_addr_list[i] != NULL; i++) free(h->h_addr_list[i]);
free(h->h_addr_list);
}
free(h);
}
static struct hostent *
copy_host(struct hostent *in)
{
if (in == NULL) return NULL;
if (in->h_addrtype == AF_INET)
return (struct hostent *)LI_ils_create("s*44a", in->h_name, in->h_aliases, in->h_addrtype, in->h_length, in->h_addr_list);
if (in->h_addrtype == AF_INET6)
return (struct hostent *)LI_ils_create("s*44c", in->h_name, in->h_aliases, in->h_addrtype, in->h_length, in->h_addr_list);
return NULL;
}
static void
_free_addr_list(char **l)
{
int i;
if (l == NULL) return;
for (i = 0; l[i] != NULL; i++) free(l[i]);
free(l);
}
static int
_map_v4(char ***v6, uint32_t n6, char **v4, uint32_t n4)
{
struct in6_addr a6;
uint32_t i;
a6.__u6_addr.__u6_addr32[0] = 0x00000000;
a6.__u6_addr.__u6_addr32[1] = 0x00000000;
a6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
if (*v6 == NULL)
{
*v6 = (char **)calloc(n4 + 1, sizeof(char *));
}
else
{
*v6 = (char **)reallocf(*v6, (n6 + n4 + 1) * sizeof(char *));
}
if (*v6 == NULL) return -1;
for (i = 0; i < n4; i++)
{
(*v6)[n6] = (char *)calloc(1, IPV6_ADDR_LEN);
if ((*v6)[n6] == NULL) return -1;
memcpy(&(a6.__u6_addr.__u6_addr32[3]), v4[i], IPV4_ADDR_LEN);
memcpy((*v6)[n6], &(a6.__u6_addr.__u6_addr32[0]), IPV6_ADDR_LEN);
n6++;
}
return 0;
}
__private_extern__ struct hostent *
extract_host(kvarray_t *in, int want)
{
struct hostent tmp, *out;
uint32_t i, d, k, kcount, vcount, v4count, v6count;
int status, addr_len;
int family, addr_count;
struct in_addr a4;
struct in6_addr a6;
char **v4addrs, **v6addrs;
char *empty[1];
v4addrs = NULL;
v6addrs = NULL;
v4count = 0;
v6count = 0;
addr_count = 0;
addr_len = sizeof(void *);
if (in == NULL) return NULL;
d = in->curr;
in->curr++;
if (d >= in->count) return NULL;
empty[0] = NULL;
memset(&tmp, 0, ENTRY_SIZE);
family = AF_INET;
tmp.h_length = IPV4_ADDR_LEN;
if (want != WANT_A4_ONLY)
{
family = AF_INET6;
tmp.h_length = IPV6_ADDR_LEN;
}
tmp.h_addrtype = family;
kcount = in->dict[d].kcount;
for (k = 0; k < kcount; k++)
{
if (!strcmp(in->dict[d].key[k], "h_name"))
{
if (tmp.h_name != NULL) continue;
vcount = in->dict[d].vcount[k];
if (vcount == 0) continue;
tmp.h_name = (char *)in->dict[d].val[k][0];
}
else if (!strcmp(in->dict[d].key[k], "h_aliases"))
{
if (tmp.h_aliases != NULL) continue;
vcount = in->dict[d].vcount[k];
if (vcount == 0) continue;
tmp.h_aliases = (char **)in->dict[d].val[k];
}
else if (!strcmp(in->dict[d].key[k], "h_ipv4_addr_list"))
{
if (v4addrs != NULL) continue;
v4count = in->dict[d].vcount[k];
if (v4count == 0) continue;
v4addrs = (char **)calloc(v4count + 1, sizeof(char *));
if (v4addrs == NULL)
{
_free_addr_list(v6addrs);
return NULL;
}
for (i = 0; i < v4count; i++)
{
v4addrs[i] = calloc(1, IPV4_ADDR_LEN);
if (v4addrs[i] == NULL)
{
_free_addr_list(v4addrs);
_free_addr_list(v6addrs);
return NULL;
}
memset(&a4, 0, sizeof(struct in_addr));
status = inet_pton(AF_INET, in->dict[d].val[k][i], &a4);
if (status != 1)
{
_free_addr_list(v4addrs);
_free_addr_list(v6addrs);
return NULL;
}
memcpy(v4addrs[i], &a4, IPV4_ADDR_LEN);
}
}
else if (!strcmp(in->dict[d].key[k], "h_ipv6_addr_list"))
{
if (v6addrs != NULL) continue;
v6count = in->dict[d].vcount[k];
if (v6count == 0) continue;
v6addrs = (char **)calloc(v6count + 1, sizeof(char *));
if (v6addrs == NULL)
{
_free_addr_list(v4addrs);
return NULL;
}
for (i = 0; i < v6count; i++)
{
v6addrs[i] = calloc(1, IPV6_ADDR_LEN);
if (v6addrs[i] == NULL)
{
_free_addr_list(v4addrs);
_free_addr_list(v6addrs);
return NULL;
}
memset(&a6, 0, sizeof(struct in6_addr));
status = inet_pton(AF_INET6, in->dict[d].val[k][i], &a6);
if (status != 1)
{
_free_addr_list(v4addrs);
_free_addr_list(v6addrs);
return NULL;
}
memcpy(v6addrs[i], &(a6.__u6_addr.__u6_addr32[0]), IPV6_ADDR_LEN);
}
}
}
if (tmp.h_name == NULL) tmp.h_name = "";
if (tmp.h_aliases == NULL) tmp.h_aliases = empty;
if (want == WANT_A4_ONLY)
{
_free_addr_list(v6addrs);
if (v4addrs == NULL) return NULL;
tmp.h_addr_list = v4addrs;
out = copy_host(&tmp);
_free_addr_list(v4addrs);
return out;
}
else if ((want == WANT_A6_ONLY) || ((want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) && (v6count > 0)))
{
_free_addr_list(v4addrs);
if (v6addrs == NULL) return NULL;
tmp.h_addr_list = v6addrs;
out = copy_host(&tmp);
_free_addr_list(v6addrs);
return out;
}
if (want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) want = WANT_MAPPED_A4_ONLY;
if (want == WANT_MAPPED_A4_ONLY)
{
_free_addr_list(v6addrs);
v6addrs = NULL;
v6count = 0;
}
status = _map_v4(&v6addrs, v6count, v4addrs, v4count);
_free_addr_list(v4addrs);
if (status != 0)
{
_free_addr_list(v6addrs);
return NULL;
}
if (v6addrs == NULL) return NULL;
tmp.h_addr_list = v6addrs;
out = copy_host(&tmp);
_free_addr_list(v6addrs);
return out;
}
__private_extern__ struct hostent *
fake_hostent(const char *name, struct in_addr addr)
{
struct hostent tmp;
char *addrs[2];
char *aliases[1];
if (name == NULL) return NULL;
memset(&tmp, 0, ENTRY_SIZE);
tmp.h_name = (char *)name;
tmp.h_addrtype = AF_INET;
tmp.h_length = IPV4_ADDR_LEN;
tmp.h_addr_list = addrs;
addrs[0] = (char *)&(addr.s_addr);
addrs[1] = NULL;
tmp.h_aliases = aliases;
aliases[0] = NULL;
return copy_host(&tmp);
}
__private_extern__ struct hostent *
fake_hostent6(const char *name, struct in6_addr addr)
{
struct hostent tmp;
char *addrs[2];
char *aliases[1];
if (name == NULL) return NULL;
memset(&tmp, 0, ENTRY_SIZE);
tmp.h_name = (char *)name;
tmp.h_addrtype = AF_INET6;
tmp.h_length = IPV6_ADDR_LEN;
tmp.h_addr_list = addrs;
addrs[0] = (char *)&(addr.__u6_addr.__u6_addr32[0]);
addrs[1] = NULL;
tmp.h_aliases = aliases;
aliases[0] = NULL;
return copy_host(&tmp);
}
static void
cache_host(struct hostent *h, int want, int how)
{
struct hostent *hcache;
if (h == NULL) return;
pthread_mutex_lock(&_host_cache_lock);
hcache = copy_host(h);
if (how == CACHE_BYNAME)
{
if (_host_byname_cache[_host_byname_cache_index] != NULL) LI_ils_free(_host_byname_cache[_host_byname_cache_index], ENTRY_SIZE);
_host_byname_cache[_host_byname_cache_index] = hcache;
_host_byname_cache_flavor[_host_byname_cache_index] = want;
_host_byname_cache_index = (_host_byname_cache_index + 1) % HOST_CACHE_SIZE;
}
else
{
if (_host_byaddr_cache[_host_byaddr_cache_index] != NULL) LI_ils_free(_host_byaddr_cache[_host_byaddr_cache_index], ENTRY_SIZE);
_host_byaddr_cache[_host_byaddr_cache_index] = hcache;
_host_byaddr_cache_flavor[_host_byaddr_cache_index] = want;
_host_byaddr_cache_index = (_host_byaddr_cache_index + 1) % HOST_CACHE_SIZE;
}
_host_cache_init = 1;
pthread_mutex_unlock(&_host_cache_lock);
}
static int
host_cache_check()
{
uint32_t i, status;
if (_host_cache_init == 0) return 1;
status = LI_L1_cache_check(ENTRY_KEY);
if ((status == LI_L1_CACHE_DISABLED) || (status == LI_L1_CACHE_FAILED)) return 1;
if (status == LI_L1_CACHE_OK) return 0;
pthread_mutex_lock(&_host_cache_lock);
for (i = 0; i < HOST_CACHE_SIZE; i++)
{
LI_ils_free(_host_byname_cache[i], ENTRY_SIZE);
_host_byname_cache[i] = NULL;
_host_byname_cache_flavor[i] = WANT_NOTHING;
LI_ils_free(_host_byaddr_cache[i], ENTRY_SIZE);
_host_byaddr_cache[i] = NULL;
_host_byaddr_cache_flavor[i] = WANT_NOTHING;
}
_host_byname_cache_index = 0;
_host_byaddr_cache_index = 0;
pthread_mutex_unlock(&_host_cache_lock);
return 1;
}
static struct hostent *
cache_gethostbyname(const char *name, int want)
{
int i;
struct hostent *h, *res;
char **aliases;
if (name == NULL) return NULL;
if (host_cache_check() != 0) return NULL;
pthread_mutex_lock(&_host_cache_lock);
for (i = 0; i < HOST_CACHE_SIZE; i++)
{
if (_host_byname_cache_flavor[i] != want) continue;
h = (struct hostent *)_host_byname_cache[i];
if (h == NULL) continue;
if (h->h_name != NULL)
{
if (!strcmp(name, h->h_name))
{
res = copy_host(h);
pthread_mutex_unlock(&_host_cache_lock);
return res;
}
}
aliases = h->h_aliases;
if (aliases == NULL)
{
pthread_mutex_unlock(&_host_cache_lock);
return NULL;
}
for (; *aliases != NULL; *aliases++)
{
if (!strcmp(name, *aliases))
{
res = copy_host(h);
pthread_mutex_unlock(&_host_cache_lock);
return res;
}
}
}
pthread_mutex_unlock(&_host_cache_lock);
return NULL;
}
static struct hostent *
cache_gethostbyaddr(const char *addr, int want)
{
int i, j, len;
struct hostent *h, *res;
if (addr == NULL) return NULL;
if (host_cache_check() != 0) return NULL;
pthread_mutex_lock(&_host_cache_lock);
len = IPV4_ADDR_LEN;
if (want > WANT_A4_ONLY) len = IPV6_ADDR_LEN;
for (i = 0; i < HOST_CACHE_SIZE; i++)
{
if (_host_byaddr_cache_flavor[i] != want) continue;
h = (struct hostent *)_host_byaddr_cache[i];
if (h == NULL) continue;
if (h->h_addr_list == NULL) continue;
for (j = 0; h->h_addr_list[j] != NULL; j++)
{
if (memcmp(addr, h->h_addr_list[j], len) == 0)
{
res = copy_host(h);
pthread_mutex_unlock(&_host_cache_lock);
return res;
}
}
}
pthread_mutex_unlock(&_host_cache_lock);
return NULL;
}
static struct hostent *
ds_gethostbyaddr(const char *paddr, uint32_t family, int *err)
{
struct hostent *entry;
kvbuf_t *request;
kvarray_t *reply;
kern_return_t status;
static int proc = -1;
struct in_addr addr4;
struct in6_addr addr6;
char tmp[64];
int want;
if (paddr == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbyaddr", &proc);
if (status != KERN_SUCCESS)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
}
memset(&addr4, 0, sizeof(struct in_addr));
memset(&addr6, 0, sizeof(struct in6_addr));
memset(tmp, 0, sizeof(tmp));
want = WANT_A4_ONLY;
if (family == AF_INET)
{
want = WANT_A4_ONLY;
memcpy(&(addr4.s_addr), paddr, IPV4_ADDR_LEN);
if (inet_ntop(family, &addr4, tmp, sizeof(tmp)) == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
}
else if (family == AF_INET6)
{
want = WANT_A6_ONLY;
memcpy(addr6.s6_addr, paddr, IPV6_ADDR_LEN);
if (inet_ntop(family, &addr6, tmp, sizeof(tmp)) == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
}
else
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
request = kvbuf_query("ksku", "address", tmp, "family", family);
if (request == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
reply = NULL;
status = LI_DSLookupQuery(proc, request, &reply);
kvbuf_free(request);
if (status != KERN_SUCCESS)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
if (err != NULL) *err = 0;
entry = extract_host(reply, want);
if ((entry == NULL) && (err != NULL)) *err = HOST_NOT_FOUND;
kvarray_free(reply);
return entry;
}
static struct hostent *
ds_gethostbyname(const char *name, uint32_t want, int *err)
{
struct hostent *entry;
kvbuf_t *request;
kvarray_t *reply;
kern_return_t status;
static int proc = -1;
uint32_t want4, want6;
want4 = 1;
want6 = 1;
if (want == WANT_A4_ONLY) want6 = 0;
else if (want == WANT_A6_ONLY) want4 = 0;
else if (WANT_MAPPED_A4_ONLY) want6 = 0;
if (name == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbyname", &proc);
if (status != KERN_SUCCESS)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
}
request = kvbuf_query("kskuku", "name", name, "ipv4", want4, "ipv6", want6);
if (request == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
reply = NULL;
status = LI_DSLookupQuery(proc, request, &reply);
kvbuf_free(request);
if (status != KERN_SUCCESS)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
if (err != NULL) *err = 0;
entry = extract_host(reply, want);
if ((entry == NULL) && (err != NULL)) *err = HOST_NOT_FOUND;
kvarray_free(reply);
return entry;
}
static void
ds_endhostent()
{
LI_data_free_kvarray(LI_data_find_key(ENTRY_KEY));
}
static void
ds_sethostent()
{
ds_endhostent();
}
static struct hostent *
ds_gethostent(int *err)
{
struct hostent *entry;
struct li_thread_info *tdata;
kvarray_t *reply, *vma;
kern_return_t status;
static int proc = -1;
tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
if (tdata == NULL)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
if (tdata->li_vm == NULL)
{
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostent", &proc);
if (status != KERN_SUCCESS)
{
if (err != NULL) *err = NO_RECOVERY;
LI_data_free_kvarray(tdata);
tdata->li_vm = NULL;
return NULL;
}
}
reply = NULL;
status = LI_DSLookupQuery(proc, NULL, &reply);
if (status != KERN_SUCCESS)
{
if (err != NULL) *err = NO_RECOVERY;
LI_data_free_kvarray(tdata);
tdata->li_vm = NULL;
return NULL;
}
tdata->li_vm = (char *)reply;
}
if (err != NULL) *err = 0;
vma = (kvarray_t *)(tdata->li_vm);
if (vma == NULL)
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
entry = NULL;
while ((vma->curr < vma->count) && (entry == NULL))
{
entry = extract_host(vma, WANT_A4_ONLY);
}
if (entry == NULL)
{
if (err != NULL) *err = HOST_NOT_FOUND;
LI_data_free_kvarray(tdata);
tdata->li_vm = NULL;
return NULL;
}
return entry;
}
static struct hostent *
gethostbyaddrerrno(const char *addr, int len, uint32_t family, int *err)
{
struct hostent *res = NULL;
int want, add_to_cache;
if (err != NULL) *err = 0;
want = WANT_A4_ONLY;
if (family == AF_INET6) want = WANT_A6_ONLY;
if ((family == AF_INET6) && (len == IPV6_ADDR_LEN) && (is_a4_mapped((const char *)addr) || is_a4_compat((const char *)addr)))
{
addr += 12;
len = 4;
family = AF_INET;
want = WANT_MAPPED_A4_ONLY;
}
add_to_cache = 0;
res = cache_gethostbyaddr(addr, want);
if (res != NULL)
{
}
else if (_ds_running())
{
res = ds_gethostbyaddr(addr, family, err);
if (res != NULL) add_to_cache = 1;
}
else
{
pthread_mutex_lock(&_host_lock);
res = copy_host(LI_files_gethostbyaddr(addr, len, family));
if (err != NULL) *err = h_errno;
pthread_mutex_unlock(&_host_lock);
}
if (add_to_cache == 1) cache_host(res, want, CACHE_BYADDR);
return res;
}
struct hostent *
gethostbyaddr(const void *addr, socklen_t len, int type)
{
struct hostent *res;
struct li_thread_info *tdata;
res = gethostbyaddrerrno(addr, len, type, &h_errno);
if (res == NULL) return NULL;
tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
if (tdata == NULL) return NULL;
LI_data_recycle(tdata, res, ENTRY_SIZE);
return (struct hostent *)tdata->li_entry;
}
struct hostent *
gethostbynameerrno(const char *name, int *err)
{
struct hostent *res = NULL;
struct in_addr addr;
int i, is_addr, add_to_cache;
if (err != NULL) *err = 0;
if (name == NULL)
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
if (name[0] == '\0')
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
is_addr = 1;
for (i = 0; name[i] != '\0'; i++)
{
if (name[i] == '.') continue;
if ((name[i] >= '0') && (name[i] <= '9')) continue;
is_addr = 0;
break;
}
if ((is_addr == 1) && (name[i-1] == '.')) is_addr = 0;
res = NULL;
add_to_cache = 0;
if (is_addr == 1)
{
if (inet_aton(name, &addr) == 0)
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
res = fake_hostent(name, addr);
}
if (res == NULL)
{
res = cache_gethostbyname(name, WANT_A4_ONLY);
}
if (res != NULL)
{
}
else if (_ds_running())
{
res = ds_gethostbyname(name, WANT_A4_ONLY, err);
if (res != NULL) add_to_cache = 1;
}
else
{
pthread_mutex_lock(&_host_lock);
res = copy_host(LI_files_gethostbyname(name));
if (err != NULL) *err = h_errno;
pthread_mutex_unlock(&_host_lock);
}
if (res == NULL)
{
if (inet_aton(name, &addr) == 0)
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
res = gethostbyaddrerrno((char *)&addr, sizeof(addr), AF_INET, err);
if (res == NULL)
{
res = fake_hostent(name, addr);
}
}
if (add_to_cache == 1) cache_host(res, WANT_A4_ONLY, CACHE_BYNAME);
return res;
}
struct hostent *
gethostbyname(const char *name)
{
struct hostent *res;
struct li_thread_info *tdata;
res = gethostbynameerrno(name, &h_errno);
if (res == NULL) return NULL;
tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
if (tdata == NULL) return NULL;
LI_data_recycle(tdata, res, ENTRY_SIZE);
return (struct hostent *)tdata->li_entry;
}
struct hostent *
gethostbyname2(const char *name, int family)
{
struct hostent *res;
struct li_thread_info *tdata;
res = getipnodebyname(name, family, 0, &h_errno);
if (res == NULL)
{
errno = EAFNOSUPPORT;
return NULL;
}
tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
if (tdata == NULL) return NULL;
LI_data_recycle(tdata, res, ENTRY_SIZE);
return (struct hostent *)tdata->li_entry;
}
struct hostent *
gethostent(void)
{
struct hostent *res = NULL;
struct li_thread_info *tdata;
tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
if (tdata == NULL) return NULL;
if (_ds_running())
{
res = ds_gethostent(&h_errno);
}
else
{
pthread_mutex_lock(&_host_lock);
res = copy_host(LI_files_gethostent());
pthread_mutex_unlock(&_host_lock);
}
LI_data_recycle(tdata, res, ENTRY_SIZE);
return (struct hostent *)tdata->li_entry;
}
void
sethostent(int stayopen)
{
if (_ds_running()) ds_sethostent();
else LI_files_sethostent(stayopen);
}
void
endhostent(void)
{
if (_ds_running()) ds_endhostent();
else LI_files_endhostent();
}
__private_extern__ int
is_a4_mapped(const char *s)
{
int i;
u_int8_t c;
if (s == NULL) return 0;
for (i = 0; i < 10; i++)
{
c = s[i];
if (c != 0x0) return 0;
}
for (i = 10; i < 12; i++)
{
c = s[i];
if (c != 0xff) return 0;
}
return 1;
}
__private_extern__ int
is_a4_compat(const char *s)
{
int i;
u_int8_t c;
if (s == NULL) return 0;
for (i = 0; i < 12; i++)
{
c = s[i];
if (c != 0x0) return 0;
}
for (i = 13; i < 15; i++)
{
c = s[i];
if (c != 0x0) return 1;
}
c = s[15];
if (c == 0x0) return 0;
if (c == 0x1) return 0;
return 1;
}
struct hostent *
getipnodebyaddr(const void *src, size_t len, int family, int *err)
{
struct hostent *res;
if (err != NULL) *err = 0;
res = gethostbyaddrerrno((const char *)src, len, family, err);
if (res == NULL) return NULL;
if (res->h_name == NULL)
{
freehostent(res);
return NULL;
}
return res;
}
struct hostent *
getipnodebyname(const char *name, int family, int flags, int *err)
{
int status, want, if4, if6, add_to_cache;
struct hostent *res;
struct ifaddrs *ifa, *ifap;
struct in_addr addr4;
struct in6_addr addr6;
memset(&addr4, 0, sizeof(struct in_addr));
memset(&addr6, 0, sizeof(struct in6_addr));
if (err != NULL) *err = 0;
if (family == AF_INET)
{
status = inet_aton(name, &addr4);
if (status == 1)
{
res = fake_hostent(name, addr4);
return res;
}
}
else if (family == AF_INET6)
{
status = inet_pton(family, name, &addr6);
if (status == 1)
{
res = fake_hostent6(name, addr6);
return res;
}
status = inet_aton(name, &addr4);
if (status == 1)
{
if (!(flags & (AI_V4MAPPED|AI_V4MAPPED_CFG)))
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
addr6.__u6_addr.__u6_addr32[0] = 0x00000000;
addr6.__u6_addr.__u6_addr32[1] = 0x00000000;
addr6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
memmove(&(addr6.__u6_addr.__u6_addr32[3]), &(addr4.s_addr), IPV4_ADDR_LEN);
res = fake_hostent6(name, addr6);
return res;
}
}
else
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
if4 = 0;
if6 = 0;
if (flags & AI_ADDRCONFIG)
{
if (getifaddrs(&ifa) < 0)
{
if (err != NULL) *err = NO_RECOVERY;
return NULL;
}
for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
{
if (ifap->ifa_addr == NULL) continue;
if ((ifap->ifa_flags & IFF_UP) == 0) continue;
if (ifap->ifa_addr->sa_family == AF_INET) if4++;
else if (ifap->ifa_addr->sa_family == AF_INET6) if6++;
}
freeifaddrs(ifa);
if ((if4 == 0) && (if6 == 0))
{
if (err != NULL) *err = NO_ADDRESS;
return NULL;
}
}
want = WANT_A4_ONLY;
if (family == AF_INET)
{
if ((flags & AI_ADDRCONFIG) && (if4 == 0))
{
if (err != NULL) *err = NO_ADDRESS;
return NULL;
}
}
else
{
want = WANT_A6_ONLY;
if (flags & (AI_V4MAPPED | AI_V4MAPPED_CFG))
{
if (flags & AI_ALL)
{
want = WANT_A6_PLUS_MAPPED_A4;
}
else
{
want = WANT_A6_OR_MAPPED_A4_IF_NO_A6;
}
}
else
{
if ((flags & AI_ADDRCONFIG) && (if6 == 0))
{
if (err != NULL) *err = NO_ADDRESS;
return NULL;
}
}
}
add_to_cache = 0;
res = cache_gethostbyname(name, want);
if (res != NULL)
{
}
else if (_ds_running())
{
res = ds_gethostbyname(name, want, err);
if (res != NULL) add_to_cache = 1;
}
else
{
pthread_mutex_lock(&_host_lock);
res = copy_host(LI_files_gethostbyname2(name, family));
if (err != NULL) *err = h_errno;
pthread_mutex_unlock(&_host_lock);
}
if (res == NULL)
{
if (err != NULL) *err = HOST_NOT_FOUND;
return NULL;
}
if (add_to_cache == 1) cache_host(res, want, CACHE_BYNAME);
return res;
}
static int
ether_extract_mac(kvarray_t *in, struct ether_addr *e)
{
uint32_t d, k, kcount, t[6];
int i;
if (in == NULL) return -1;
if (e == NULL) return -1;
d = in->curr;
in->curr++;
if (d >= in->count) return -1;
kcount = in->dict[d].kcount;
for (k = 0; k < kcount; k++)
{
if (!strcmp(in->dict[d].key[k], "mac"))
{
if (in->dict[d].vcount[k] == 0) continue;
i = sscanf(in->dict[d].val[k][0], " %x:%x:%x:%x:%x:%x", &t[0], &t[1], &t[2], &t[3], &t[4], &t[5]);
if (i != 6) return -1;
for (i = 0; i < 6; i++) e->ether_addr_octet[i] = t[i];
return 0;
}
}
return -1;
}
static int
ds_ether_hostton(const char *host, struct ether_addr *e)
{
static int proc = -1;
kvbuf_t *request;
kvarray_t *reply;
kern_return_t status;
if (host == NULL) return -1;
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("getmacbyname", &proc);
if (status != KERN_SUCCESS) return -1;
}
request = kvbuf_query_key_val("name", host);
if (request == NULL) return -1;
reply = NULL;
status = LI_DSLookupQuery(proc, request, &reply);
kvbuf_free(request);
if (status != KERN_SUCCESS) return -1;
status = ether_extract_mac(reply, e);
kvarray_free(reply);
return status;
}
static int
ether_extract_name(kvarray_t *in, char *name)
{
uint32_t d, k, kcount;
if (in == NULL) return -1;
if (name == NULL) return -1;
d = in->curr;
in->curr++;
if (d >= in->count) return -1;
kcount = in->dict[d].kcount;
for (k = 0; k < kcount; k++)
{
if (!strcmp(in->dict[d].key[k], "name"))
{
memcpy(name, in->dict[d].val[k][0], strlen(in->dict[d].val[k][0]) + 1);
return 0;
}
}
return -1;
}
static int
ds_ether_ntohost(char *host, const struct ether_addr *e)
{
uint32_t i, x[6];
char str[256];
static int proc = -1;
kvbuf_t *request;
kvarray_t *reply;
kern_return_t status;
if (host == NULL) return -1;
if (e == NULL) return -1;
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbymac", &proc);
if (status != KERN_SUCCESS) return -1;
}
for (i = 0; i < 6; i++) x[i] = e->ether_addr_octet[i];
sprintf(str, "%x:%x:%x:%x:%x:%x", x[0], x[1], x[2], x[3], x[4], x[5]);
request = kvbuf_query_key_val("mac", str);
if (request == NULL) return -1;
reply = NULL;
status = LI_DSLookupQuery(proc, request, &reply);
kvbuf_free(request);
if (status != KERN_SUCCESS) return -1;
status = ether_extract_name(reply, host);
kvarray_free(reply);
return status;
}
int
ether_hostton(const char *host, struct ether_addr *e)
{
if (_ds_running()) return ds_ether_hostton(host, e);
return _old_ether_hostton(host, e);
}
int
ether_ntohost(char *host, const struct ether_addr *e)
{
if (_ds_running()) return ds_ether_ntohost(host, e);
return _old_ether_ntohost(host, e);
}