#include <netdb.h>
#include <netdb_async.h>
#include <pthread.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <errno.h>
#include "lu_host.h"
#include "lu_utils.h"
#define IPV6_ADDR_LEN 16
#define IPV4_ADDR_LEN 4
typedef struct
{
void *user_context;
int want;
} my_context_t;
mach_port_t
gethostbyaddr_async_start(const char *addr, int len, int family, gethostbyaddr_async_callback callback, void *context)
{
static int proc = 1;
int32_t want, status;
kvbuf_t *request;
mach_port_t mp;
my_context_t *my_context;
mp = MACH_PORT_NULL;
if (addr == NULL) return MACH_PORT_NULL;
if (len == 0) return MACH_PORT_NULL;
if ((family != AF_INET) && (family != AF_INET6)) return MACH_PORT_NULL;
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;
}
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbyaddr", &proc);
if (status != KERN_SUCCESS) return MACH_PORT_NULL;
}
request = kvbuf_query("ksku", "address", addr, "family", want);
if (request == NULL) return MACH_PORT_NULL;
my_context = (my_context_t *)calloc(1, sizeof(my_context_t));
if (my_context == NULL) return MACH_PORT_NULL;
my_context->user_context = context;
my_context->want = want;
status = LI_async_start(&mp, proc, request, (void *)callback, my_context);
kvbuf_free(request);
return mp;
}
void
gethostbyaddr_async_cancel(mach_port_t port)
{
my_context_t *my_context;
my_context = NULL;
LI_async_call_cancel(port, (void **)&my_context);
if (my_context != NULL) free(my_context);
}
void
gethostbyaddr_async_handleReply(void *msg)
{
gethostbyaddr_async_callback callback;
struct hostent *out;
uint32_t len, want;
int status;
kvarray_t *reply;
my_context_t *my_context;
void *context;
callback = (gethostbyaddr_async_callback)NULL;
my_context = NULL;
context = NULL;
len = 0;
reply = NULL;
status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&my_context);
if ((status != KERN_SUCCESS) || (reply == NULL))
{
if (status == MIG_REPLY_MISMATCH) return;
if (callback != NULL)
{
if (my_context != NULL) context = my_context->user_context;
callback(NULL, context);
free(my_context);
return;
}
}
want = WANT_A4_ONLY;
if (my_context != NULL)
{
context = my_context->user_context;
want = my_context->want;
free(my_context);
}
out = extract_host(reply, want);
kvarray_free(reply);
callback(out, context);
}
mach_port_t
getipnodebyaddr_async_start(const void *addr, size_t len, int family, int *error, getipnodebyaddr_async_callback callback, void *context)
{
static int proc = 1;
int32_t want, status;
kvbuf_t *request;
mach_port_t mp;
my_context_t *my_context;
mp = MACH_PORT_NULL;
if (addr == NULL) return MACH_PORT_NULL;
if (len == 0) return MACH_PORT_NULL;
if ((family != AF_INET) && (family != AF_INET6)) return MACH_PORT_NULL;
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;
}
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbyaddr", &proc);
if (status != KERN_SUCCESS) return MACH_PORT_NULL;
}
request = kvbuf_query("ksku", "address", addr, "family", want);
if (request == NULL) return MACH_PORT_NULL;
my_context = (my_context_t *)calloc(1, sizeof(my_context_t));
if (my_context == NULL) return MACH_PORT_NULL;
my_context->user_context = context;
my_context->want = want;
status = LI_async_start(&mp, proc, request, (void *)callback, my_context);
kvbuf_free(request);
return mp;
}
void
getipnodebyaddr_async_cancel(mach_port_t port)
{
my_context_t *my_context;
my_context = NULL;
LI_async_call_cancel(port, (void **)&my_context);
if (my_context != NULL) free(my_context);
}
void
getipnodebyaddr_async_handleReply(void *msg)
{
getipnodebyaddr_async_callback callback;
struct hostent *out;
uint32_t len, want;
int status;
kvarray_t *reply;
my_context_t *my_context;
void *context;
callback = (getipnodebyaddr_async_callback)NULL;
my_context = NULL;
context = NULL;
len = 0;
reply = NULL;
status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&my_context);
if ((status != KERN_SUCCESS) || (reply == NULL))
{
if (status == MIG_REPLY_MISMATCH) return;
if (callback != NULL)
{
if (my_context != NULL) context = my_context->user_context;
callback(NULL, NO_RECOVERY, context);
free(my_context);
return;
}
}
want = WANT_A4_ONLY;
if (my_context != NULL)
{
context = my_context->user_context;
want = my_context->want;
free(my_context);
}
out = extract_host(reply, want);
kvarray_free(reply);
if (out == NULL)
{
callback(NULL, HOST_NOT_FOUND, context);
return;
}
callback(out, 0, context);
}
mach_port_t
gethostbyname_async_start(const char *name, gethostbyname_async_callback callback, void *context)
{
static int proc = 1;
int32_t status;
kvbuf_t *request;
mach_port_t mp;
mp = MACH_PORT_NULL;
if (name == NULL) return MACH_PORT_NULL;
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbyname", &proc);
if (status != KERN_SUCCESS) return MACH_PORT_NULL;
}
request = kvbuf_query("ksksks", "name", name, "ipv4", "1", "ipv6", "0");
if (request == NULL) return MACH_PORT_NULL;
status = LI_async_start(&mp, proc, request, (void *)callback, context);
kvbuf_free(request);
return mp;
}
void
gethostbyname_async_cancel(mach_port_t port)
{
LI_async_call_cancel(port, NULL);
}
void
gethostbyname_async_handleReply(void *msg)
{
gethostbyname_async_callback callback;
struct hostent *out;
uint32_t len;
int status;
kvarray_t *reply;
void *context;
callback = (gethostbyname_async_callback)NULL;
context = NULL;
len = 0;
reply = NULL;
status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&context);
if ((status != KERN_SUCCESS) || (reply == NULL))
{
if (status == MIG_REPLY_MISMATCH) return;
if (callback != NULL)
{
callback(NULL, context);
return;
}
}
out = extract_host(reply, AF_INET);
kvarray_free(reply);
callback(out, context);
}
mach_port_t
getipnodebyname_async_start(const char *name, int family, int flags, int *err, getipnodebyname_async_callback callback, void *context)
{
static int proc = 1;
int32_t status, want, want4, want6, if4, if6;
kvbuf_t *request;
mach_port_t mp;
struct ifaddrs *ifa, *ifap;
struct in_addr addr4;
struct in6_addr addr6;
my_context_t *my_context;
if (name == NULL) return MACH_PORT_NULL;
if (err != NULL) *err = 0;
if4 = 0;
if6 = 0;
mp = MACH_PORT_NULL;
memset(&addr4, 0, sizeof(struct in_addr));
memset(&addr6, 0, sizeof(struct in6_addr));
if (flags & AI_ADDRCONFIG)
{
if (getifaddrs(&ifa) < 0)
{
if (err != NULL) *err = NO_RECOVERY;
return MACH_PORT_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_RECOVERY;
return MACH_PORT_NULL;
}
}
want = WANT_A4_ONLY;
if (family == AF_INET)
{
if ((flags & AI_ADDRCONFIG) && (if4 == 0))
{
if (err != NULL) *err = NO_RECOVERY;
return MACH_PORT_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_RECOVERY;
return MACH_PORT_NULL;
}
}
}
if (proc < 0)
{
status = LI_DSLookupGetProcedureNumber("gethostbyname", &proc);
if (status != KERN_SUCCESS) return MACH_PORT_NULL;
}
my_context = (my_context_t *)calloc(1, sizeof(my_context_t));
if (my_context == NULL)
{
*err = NO_RECOVERY;
return MACH_PORT_NULL;
}
my_context->user_context = context;
my_context->want = want;
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;
request = kvbuf_query("kskuku", "name", name, "ipv4", want4, "ipv6", want6);
if (request == NULL) return MACH_PORT_NULL;
status = LI_async_start(&mp, proc, request, (void *)callback, my_context);
kvbuf_free(request);
return mp;
}
void
getipnodebyname_async_cancel(mach_port_t port)
{
my_context_t *my_context;
my_context = NULL;
LI_async_call_cancel(port, (void **)&my_context);
if (my_context != NULL) free(my_context);
}
void
getipnodebyname_async_handleReply(void *msg)
{
getipnodebyname_async_callback callback;
struct hostent *out;
uint32_t len, want;
int status, err;
kvarray_t *reply;
my_context_t *my_context;
void *context;
callback = (getipnodebyname_async_callback)NULL;
my_context = NULL;
context = NULL;
len = 0;
reply = NULL;
status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&my_context);
if ((status != KERN_SUCCESS) || (reply == NULL))
{
if (status == MIG_REPLY_MISMATCH) return;
if (callback != NULL)
{
if (my_context != NULL) context = my_context->user_context;
callback(NULL, NO_RECOVERY, context);
free(my_context);
return;
}
}
want = WANT_A4_ONLY;
if (my_context != NULL)
{
context = my_context->user_context;
want = my_context->want;
free(my_context);
}
out = extract_host(reply, want);
kvarray_free(reply);
if (out == NULL)
{
err = HOST_NOT_FOUND;
callback(NULL, err, context);
return;
}
callback(out, 0, context);
}