#include <netdb.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <string.h>
#include <sys/param.h>
#include <notify.h>
#include <notify_keys.h>
#include <pthread.h>
#include <TargetConditionals.h>
#include "netdb_async.h"
#include "si_module.h"
#define SOCK_UNSPEC 0
#define IPPROTO_UNSPEC 0
#define IPV6_ADDR_LEN 16
#define IPV4_ADDR_LEN 4
#define WANT_NOTHING 0
#define WANT_A4_ONLY 1
#define WANT_A6_ONLY 2
#define WANT_A6_PLUS_MAPPED_A4 3
#define WANT_A6_OR_MAPPED_A4_IF_NO_A6 4
#define TEREDO_PREFIX_32 0x20010000
#define V6TO4_PREFIX_16 0x2002
#define ULA_PREFIX_8 0xfc
#define NET_TYPE_UNKNOWN 0x00000000
#define NET_TYPE_V4 0x00000001
#define NET_TYPE_V6 0x00000002
#define NET_TYPE_TEREDO 0x00000012
#define NET_TYPE_6TO4 0x00000022
#define NET_TYPE_LINKLOCAL 0x00000042
#define NET_TYPE_SITELOCAL 0x00000082
#define NET_TYPE_ULA 0x00000102
#define NET_MASK_T6LSU 0x000001f0
typedef struct
{
int v4_count;
int v6_count;
int v6to4_count;
int teredo_count;
int ula_count;
int ll_count;
int sl_count;
int gai_v6_preferred;
} config_stats_t;
static int net_config_token = -1;
static config_stats_t config_stats_global;
static pthread_mutex_t net_config_mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct {
struct hostent host;
int alias_count;
int addr_count;
uint64_t ttl;
} build_hostent_t;
static int
_si_net_addr_type(const struct sockaddr *s)
{
const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *)s;
if (s == NULL) return NET_TYPE_UNKNOWN;
else if (s->sa_family == AF_INET) return NET_TYPE_V4;
else if (s->sa_family != AF_INET6) return NET_TYPE_UNKNOWN;
else if (sa6->sin6_addr.__u6_addr.__u6_addr32[0] == ntohs(TEREDO_PREFIX_32)) return NET_TYPE_TEREDO;
else if (sa6->sin6_addr.__u6_addr.__u6_addr16[0] == ntohs(V6TO4_PREFIX_16)) return NET_TYPE_6TO4;
else if (IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))) return NET_TYPE_LINKLOCAL;
else if (IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr))) return NET_TYPE_SITELOCAL;
else if ((sa6->sin6_addr.__u6_addr.__u6_addr8[0] & 0xfe) == ULA_PREFIX_8) return NET_TYPE_ULA;
return NET_TYPE_V6;
}
static int
_si_net_type_is_IPv6_globably_reachable_non_transitional(int t)
{
if (t & NET_TYPE_V6)
{
if (t & NET_MASK_T6LSU) return 0;
return 1;
}
return 0;
}
static int
_si_netconfig(config_stats_t *stats)
{
int status, checkit, net_type;
struct ifaddrs *ifa, *ifap;
if (stats == NULL) return 0;
pthread_mutex_lock(&net_config_mutex);
status = 0;
checkit = 1;
if (net_config_token < 0)
{
status = notify_register_check(kNotifySCNetworkChange, &net_config_token);
if (status != 0) net_config_token = -1;
}
if (net_config_token >= 0)
{
status = notify_check(net_config_token, &checkit);
if (status != 0) checkit = 1;
}
if (checkit != 0)
{
if (getifaddrs(&ifa) < 0)
{
status = -1;
}
else
{
memset(&config_stats_global, 0, sizeof(config_stats_t));
for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
{
if (ifap->ifa_addr == NULL) continue;
if ((ifap->ifa_flags & IFF_UP) == 0) continue;
net_type = _si_net_addr_type(ifap->ifa_addr);
if (net_type == NET_TYPE_UNKNOWN)
{
}
else if (net_type == NET_TYPE_V4)
{
config_stats_global.v4_count++;
}
else
{
config_stats_global.v6_count++;
if (net_type == NET_TYPE_LINKLOCAL)
{
config_stats_global.ll_count++;
continue;
}
else if (net_type == NET_TYPE_ULA)
{
config_stats_global.ula_count++;
if (!strncmp(ifap->ifa_name, "utun", 4)) continue;
}
if (net_type == NET_TYPE_TEREDO)
{
config_stats_global.teredo_count++;
}
else if (net_type == NET_TYPE_6TO4)
{
config_stats_global.v6to4_count++;
}
else if (net_type == NET_TYPE_SITELOCAL)
{
config_stats_global.sl_count++;
}
if (_si_net_type_is_IPv6_globably_reachable_non_transitional(net_type))
{
config_stats_global.gai_v6_preferred = 1;
}
}
}
freeifaddrs(ifa);
}
}
*stats = config_stats_global;
pthread_mutex_unlock(&net_config_mutex);
return status;
}
__private_extern__ int
si_inet_config(uint32_t *inet4, uint32_t *inet6)
{
config_stats_t x;
if (_si_netconfig(&x) < 0) return -1;
if (inet4 != NULL) *inet4 = x.v4_count;
if (inet6 != NULL) *inet6 = x.v6_count;
return 0;
}
void
freeaddrinfo(struct addrinfo *a)
{
struct addrinfo *next;
while (a != NULL)
{
next = a->ai_next;
if (a->ai_addr != NULL) free(a->ai_addr);
if (a->ai_canonname != NULL) free(a->ai_canonname);
free(a);
a = next;
}
}
const char *
gai_strerror(int32_t err)
{
switch (err)
{
case EAI_ADDRFAMILY: return "Address family for nodename not supported";
case EAI_AGAIN: return "Temporary failure in name resolution";
case EAI_BADFLAGS: return "Invalid value for ai_flags";
case EAI_FAIL: return "Non-recoverable failure in name resolution";
case EAI_FAMILY: return "ai_family not supported";
case EAI_MEMORY: return "Memory allocation failure";
case EAI_NODATA: return "No address associated with nodename";
case EAI_NONAME: return "nodename nor servname provided, or not known";
case EAI_SERVICE: return "servname not supported for ai_socktype";
case EAI_SOCKTYPE: return "ai_socktype not supported";
case EAI_SYSTEM: return "System error";
case EAI_BADHINTS: return "Bad hints";
case EAI_PROTOCOL: return "ai_protocol not supported";
case EAI_OVERFLOW: return "argument buffer overflow";
}
return "Unknown error";
}
__private_extern__ si_item_t *
si_nameinfo(si_mod_t *si, const struct sockaddr *sa, int flags, const char *interface, uint32_t *err)
{
si_item_t *out = NULL;
const struct sockaddr *lookup_sa;
struct sockaddr_in s4;
struct in_addr a4;
struct in6_addr a6;
const uint64_t unused = 0;
void *addr = NULL;
char *host = NULL;
char *serv = NULL;
uint32_t ifnum = 0;
uint16_t port = 0;
int do_host_lookup = ((flags & NI_NUMERICHOST) == 0);
int do_serv_lookup = ((flags & NI_NUMERICSERV) == 0);
if ((si == NULL) || (sa == NULL))
{
if (err != NULL) *err = SI_STATUS_EAI_FAIL;
return NULL;
}
if (err != NULL) *err = SI_STATUS_NO_ERROR;
lookup_sa = sa;
if (sa->sa_family == AF_INET)
{
struct sockaddr_in *s4 = (struct sockaddr_in *)sa;
memcpy(&a4, &s4->sin_addr, sizeof(a4));
port = s4->sin_port;
addr = &a4;
}
else if (sa->sa_family == AF_INET6)
{
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)sa;
memcpy(&a6, &s6->sin6_addr, sizeof(a6));
port = s6->sin6_port;
if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr))
{
ifnum = ntohs(a6.__u6_addr.__u6_addr16[1]);
if (ifnum == 0)
{
ifnum = s6->sin6_scope_id;
a6.__u6_addr.__u6_addr16[1] = htons(ifnum);
}
if (ifnum != s6->sin6_scope_id &&
s6->sin6_scope_id != 0)
{
if (err != NULL) *err = SI_STATUS_EAI_FAIL;
return NULL;
}
}
if (IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr) || IN6_IS_ADDR_V4COMPAT(&s6->sin6_addr))
{
memcpy(&a4, &s6->sin6_addr.s6_addr[12], sizeof(a4));
addr = &a4;
memset(&s4, 0, sizeof(s4));
s4.sin_len = sizeof(s4);
s4.sin_family = AF_INET;
s4.sin_port = port;
memcpy(&s4.sin_addr, &a4, sizeof(s4.sin_addr));
lookup_sa = (const struct sockaddr *)&s4;
}
else
{
addr = &a6;
}
}
else
{
if (err != NULL) *err = SI_STATUS_EAI_FAMILY;
return NULL;
}
if (do_host_lookup == 1)
{
#if 0
if ((do_serv_lookup == 1) && (si->sim_nameinfo != NULL))
{
return si->sim_nameinfo(si, lookup_sa, flags, interface, err);
}
#endif
si_item_t *item = si_host_byaddr(si, addr, lookup_sa->sa_family, interface, NULL);
if (item != NULL)
{
struct hostent *h;
h = (struct hostent *)((uintptr_t)item + sizeof(si_item_t));
host = strdup(h->h_name);
si_item_release(item);
if (host == NULL)
{
if (err != NULL) *err = SI_STATUS_EAI_MEMORY;
return NULL;
}
}
}
if ((do_serv_lookup == 1) && (port != 0))
{
si_item_t *item = si_service_byport(si, port, NULL);
if (item != NULL)
{
struct servent *s;
s = (struct servent *)((uintptr_t)item + sizeof(si_item_t));
serv = strdup(s->s_name);
si_item_release(item);
if (serv == NULL)
{
free(host);
if (err != NULL) *err = SI_STATUS_EAI_MEMORY;
return NULL;
}
}
}
if (host == NULL && (flags & NI_NAMEREQD) == 0)
{
char tmp[INET6_ADDRSTRLEN + 1 + IF_NAMESIZE + 1];
tmp[0] = '\0';
if (sa->sa_family == AF_INET)
{
char buf[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &a4, buf, sizeof(buf)) != 0)
{
host = strdup(buf);
}
}
else if (sa->sa_family == AF_INET6)
{
char buf[INET6_ADDRSTRLEN];
if (ifnum != 0)
{
a6.__u6_addr.__u6_addr16[1] = 0;
}
if (inet_ntop(AF_INET6, &a6, buf, sizeof(buf)) != 0)
{
if (ifnum != 0)
{
char ifname[IF_NAMESIZE];
if (if_indextoname(ifnum, ifname) != NULL)
{
asprintf(&host, "%s%%%s", buf, ifname);
}
else
{
if (err != NULL) *err = SI_STATUS_EAI_FAIL;
return NULL;
}
}
else
{
host = strdup(buf);
}
}
}
}
if (serv == NULL)
{
asprintf(&serv, "%hu", ntohs(port));
}
if ((host == NULL) || (serv == NULL))
{
if (err != NULL)
{
if ((flags & NI_NAMEREQD) != 0)
{
*err = SI_STATUS_EAI_NONAME;
}
else
{
*err = SI_STATUS_EAI_MEMORY;
}
}
}
else
{
out = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_NAMEINFO, 1, unused, unused, host, serv);
}
free(host);
free(serv);
return out;
}
static int
_gai_numericserv(const char *serv, uint16_t *port)
{
int numeric;
char *endptr;
long num;
numeric = 0;
if (serv == NULL)
{
if (port) *port = 0;
numeric = 1;
}
else
{
num = strtol(serv, &endptr, 10);
if ((serv[0] != '\0') && (*endptr == '\0') && (num >= 0) && (num <= UINT16_MAX))
{
numeric = 1;
if (port != NULL) *port = (uint16_t)num;
}
}
return numeric;
}
__private_extern__ int
_gai_serv_to_port(const char *serv, uint32_t proto, uint16_t *port)
{
si_item_t *item;
struct servent *s;
const char *protoname = NULL;
if (_gai_numericserv(serv, port)) return 0;
if (proto == IPPROTO_UDP) protoname = "udp";
if (proto == IPPROTO_TCP) protoname = "tcp";
item = si_service_byname(si_search(), serv, protoname);
if (item == NULL) return -1;
s = (struct servent *)((uintptr_t)item + sizeof(si_item_t));
if (port) *port = ntohs(s->s_port);
si_item_release(item);
return 0;
}
__private_extern__ si_item_t *
si_addrinfo_v4(si_mod_t *si, int32_t flags, int32_t sock, int32_t proto, uint16_t port, struct in_addr *addr, uint16_t iface, const char *cname)
{
socket_data_t sockdata;
struct sockaddr_in *sa;
int32_t len, v32;
uint64_t unused;
unused = 0;
len = sizeof(struct sockaddr_in);
memset(&sockdata, 0, sizeof(socket_data_t));
sa = (struct sockaddr_in *)&sockdata;
sa->sin_len = len;
sa->sin_family = AF_INET;
sa->sin_port = htons(port);
memcpy(&sa->sin_addr, addr, sizeof(sa->sin_addr));
v32 = iface;
memmove(sa->sin_zero, &v32, sizeof(uint32_t));
return (si_item_t *)LI_ils_create("L448844444Ss", (unsigned long)si, CATEGORY_ADDRINFO, 1, unused, unused, flags, AF_INET, sock, proto, len, sockdata, cname);
}
__private_extern__ si_item_t *
si_addrinfo_v6(si_mod_t *si, int32_t flags, int32_t sock, int32_t proto, uint16_t port, struct in6_addr *addr, uint16_t iface, const char *cname)
{
socket_data_t sockdata;
struct sockaddr_in6 *sa;
int32_t len;
uint64_t unused;
unused = 0;
len = sizeof(struct sockaddr_in6);
memset(&sockdata, 0, sizeof(socket_data_t));
sa = (struct sockaddr_in6 *)&sockdata;
sa->sin6_len = len;
sa->sin6_family = AF_INET6;
sa->sin6_port = htons(port);
memcpy(&sa->sin6_addr, addr, sizeof(sa->sin6_addr));
sa->sin6_scope_id = iface;
if (IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
{
uint16_t esid = ntohs(sa->sin6_addr.__u6_addr.__u6_addr16[1]);
if (esid != 0)
{
sa->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
if (iface == 0) sa->sin6_scope_id = esid;
}
}
return (si_item_t *)LI_ils_create("L448844444Ss", (unsigned long)si, CATEGORY_ADDRINFO, 1, unused, unused, flags, AF_INET6, sock, proto, len, sockdata, cname);
}
__private_extern__ si_list_t *
si_addrinfo_list(si_mod_t *si, int socktype, int proto, struct in_addr *a4, struct in6_addr *a6, int port, int scopeid, const char *cname4, const char *cname6)
{
si_item_t *item = NULL;
si_list_t *out4 = NULL, *out6 = NULL;
if (a4 != NULL)
{
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
{
item = si_addrinfo_v4(si, 0, SOCK_DGRAM, IPPROTO_UDP, port, a4, 0, cname4);
out4 = si_list_add(out4, item);
si_item_release(item);
}
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
{
item = si_addrinfo_v4(si, 0, SOCK_STREAM, IPPROTO_TCP, port, a4, 0, cname4);
out4 = si_list_add(out4, item);
si_item_release(item);
}
if (proto == IPPROTO_ICMP)
{
item = si_addrinfo_v4(si, 0, SOCK_RAW, IPPROTO_ICMP, port, a4, 0, cname4);
out4 = si_list_add(out4, item);
si_item_release(item);
}
}
if (a6 != NULL)
{
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
{
item = si_addrinfo_v6(si, 0, SOCK_DGRAM, IPPROTO_UDP, port, a6, scopeid, cname6);
out6 = si_list_add(out6, item);
si_item_release(item);
}
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
{
item = si_addrinfo_v6(si, 0, SOCK_STREAM, IPPROTO_TCP, port, a6, scopeid, cname6);
out6 = si_list_add(out6, item);
si_item_release(item);
}
if (proto == IPPROTO_ICMPV6)
{
item = si_addrinfo_v6(si, 0, SOCK_RAW, IPPROTO_ICMPV6, port, a6, scopeid, cname6);
out6 = si_list_add(out6, item);
si_item_release(item);
}
}
out6 = si_list_concat(out6, out4);
si_list_release(out4);
return out6;
}
static int
_gai_numerichost(const char* nodename, uint32_t *family, int flags, struct in_addr *a4, struct in6_addr *a6)
{
int numerichost, passive;
numerichost = 0;
if (nodename == NULL)
{
passive = (flags & AI_PASSIVE);
if ((*family == AF_UNSPEC) || (*family == AF_INET))
{
if (passive) a4->s_addr = 0;
else a4->s_addr = htonl(INADDR_LOOPBACK);
}
if ((*family == AF_UNSPEC) || (*family == AF_INET6))
{
memset(a6, 0, sizeof(*a6));
if (!passive) a6->__u6_addr.__u6_addr32[3] = htonl(1);
}
numerichost = 1;
}
else
{
numerichost = inet_pton(AF_INET, nodename, a4);
if (numerichost == 1)
{
if (*family == AF_UNSPEC) *family = AF_INET;
else if (*family == AF_INET6) numerichost = -1;
return numerichost;
}
numerichost = inet_pton(AF_INET6, nodename, a6);
if (numerichost == 1)
{
if (*family == AF_UNSPEC) *family = AF_INET6;
else if (*family == AF_INET) numerichost = -1;
return numerichost;
}
}
return numerichost;
}
__private_extern__ si_list_t *
si_addrinfo_list_from_hostent(si_mod_t *si, uint32_t socktype, uint32_t proto, uint16_t port, uint16_t scope, const struct hostent *h4, const struct hostent *h6)
{
int i;
si_list_t *out = NULL;
si_list_t *list;
if ((h6 != NULL) && (h6->h_addr_list != NULL))
{
for (i = 0; h6->h_addr_list[i] != NULL; i++)
{
struct in6_addr a6;
memcpy(&a6, h6->h_addr_list[i], h6->h_length);
list = si_addrinfo_list(si, socktype, proto, NULL, &a6, port, scope, NULL, h6->h_name);
out = si_list_concat(out, list);
si_list_release(list);
}
}
if ((h4 != NULL) && (h4->h_addr_list != NULL))
{
for (i = 0; h4->h_addr_list[i] != NULL; i++)
{
struct in_addr a4;
memcpy(&a4, h4->h_addr_list[i], h4->h_length);
list = si_addrinfo_list(si, socktype, proto, &a4, NULL, port, 0, h4->h_name, NULL);
out = si_list_concat(out, list);
si_list_release(list);
}
}
return out;
}
int
_gai_sa_dst_compare(const struct sockaddr *a, const struct sockaddr *b, int unused)
{
int ta, tb, posn_a, posn_b;
ta = _si_net_addr_type(a);
tb = _si_net_addr_type(b);
posn_a = 2;
posn_b = 2;
if (ta & NET_TYPE_V4) posn_a = 1;
if (tb & NET_TYPE_V4) posn_b = 1;
if (_si_net_type_is_IPv6_globably_reachable_non_transitional(ta)) posn_a = 0;
if (_si_net_type_is_IPv6_globably_reachable_non_transitional(tb)) posn_b = 0;
return (posn_a - posn_b);
}
int
_gai_addr_sort(void *thunk, const void *a, const void *b)
{
si_item_t **ia, **ib;
si_addrinfo_t *p, *q;
struct sockaddr *sp, *sq;
uint32_t *v4first = (uint32_t *)thunk;
ia = (si_item_t **)a;
ib = (si_item_t **)b;
p = (si_addrinfo_t *)((uintptr_t)*ia + sizeof(si_item_t));
q = (si_addrinfo_t *)((uintptr_t)*ib + sizeof(si_item_t));
sp = (struct sockaddr *)p->ai_addr.x;
sq = (struct sockaddr *)q->ai_addr.x;
if (*v4first == 1)
{
if (sp->sa_family == sq->sa_family) return 0;
if (sp->sa_family == AF_INET) return -1;
return 1;
}
return _gai_sa_dst_compare(sp, sq, 0);
}
static si_list_t *
_gai_sort_list(si_list_t *in)
{
uint32_t v4first;
config_stats_t x;
if (in == NULL) return NULL;
memset(&x, 0, sizeof(config_stats_t));
if (_si_netconfig(&x) < 0) return in;
v4first = 1;
if (x.gai_v6_preferred != 0) v4first = 0;
qsort_r(&in->entry[0], in->count, sizeof(si_item_t *), (void *)&v4first, _gai_addr_sort);
return in;
}
__private_extern__ si_list_t *
_gai_simple(si_mod_t *si, const void *nodeptr, const void *servptr, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
{
si_item_t *h4_item = NULL, *h6_item = NULL;
struct hostent *h4 = NULL, *h6 = NULL;
si_list_t *out = NULL;
uint16_t port;
if ((flags & AI_NUMERICSERV) != 0)
{
port = *(uint16_t*)servptr;
}
else
{
if (_gai_serv_to_port(servptr, proto, &port) != 0)
{
if (err) *err = SI_STATUS_EAI_NONAME;
return NULL;
}
}
if ((flags & AI_NUMERICHOST) != 0)
{
if (family == AF_INET)
{
h4_item = si_host_byaddr(si, nodeptr, AF_INET, interface, NULL);
}
else if (family == AF_INET6)
{
h6_item = si_host_byaddr(si, nodeptr, AF_INET6, interface, NULL);
}
}
else
{
if ((family == AF_INET) || (family == AF_UNSPEC))
{
h4_item = si_host_byname(si, nodeptr, AF_INET, interface, NULL);
}
if ((family == AF_INET6) || (family == AF_UNSPEC))
{
h6_item = si_host_byname(si, nodeptr, AF_INET6, interface, NULL);
}
}
if (h4_item != NULL)
{
h4 = (struct hostent *)((uintptr_t)h4_item + sizeof(si_item_t));
}
if (h6_item != NULL)
{
h6 = (struct hostent *)((uintptr_t)h6_item + sizeof(si_item_t));
}
out = si_addrinfo_list_from_hostent(si, socktype, proto, port, 0, h4, h6);
si_item_release(h4_item);
si_item_release(h6_item);
return _gai_sort_list(out);
}
__private_extern__ si_list_t *
si_srv_byname(si_mod_t *si, const char *qname, const char *interface, uint32_t *err)
{
if (si == NULL) return 0;
if (si->sim_srv_byname == NULL) return 0;
return si->sim_srv_byname(si, qname, interface, err);
}
__private_extern__ int
si_wants_addrinfo(si_mod_t *si)
{
if (si == NULL) return 0;
if (si->sim_addrinfo == NULL) return 0;
if (si->sim_wants_addrinfo == NULL) return 0;
return si->sim_wants_addrinfo(si);
}
static si_list_t *
_gai_srv(si_mod_t *si, const char *node, const char *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
{
int i;
char *qname;
si_srv_t *srv;
si_item_t *item;
si_list_t *list = NULL;
si_list_t *result = NULL;
int lastprio = -1;
int currprio;
if (node == NULL || serv == NULL) return NULL;
asprintf(&qname, "%s.%s", serv, node);
list = si_srv_byname(si, qname, interface, err);
free(qname);
while (list != NULL && result == NULL)
{
currprio = INT_MAX;
for (i = 0; i < list->count; ++i)
{
item = list->entry[i];
srv = (si_srv_t *)((uintptr_t)item + sizeof(si_item_t));
if (srv->priority > lastprio && srv->priority < currprio)
{
currprio = srv->priority;
}
}
if (currprio == INT_MAX)
{
break;
}
else
{
lastprio = currprio;
}
for (i = 0; i < list->count; ++i)
{
item = list->entry[i];
srv = (si_srv_t *)((uintptr_t)item + sizeof(si_item_t));
if (srv->priority == currprio)
{
flags |= AI_NUMERICSERV;
result = _gai_simple(si, srv->target, &srv->port, family, socktype, proto, flags, interface, err);
if (result)
{
break;
}
}
}
}
if (list != NULL)
{
si_list_release(list);
}
return result;
}
__private_extern__ si_list_t *
si_addrinfo(si_mod_t *si, const char *node, const char *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
{
int numerichost, numericserv = 0;
const void *nodeptr = NULL, *servptr = NULL;
uint16_t port;
struct in_addr a4, *p4;
struct in6_addr a6, *p6;
const char *cname;
si_list_t *out;
if (err != NULL) *err = SI_STATUS_NO_ERROR;
if (si == NULL)
{
if (err != NULL) *err = SI_STATUS_EAI_FAIL;
return NULL;
}
if ((node != NULL) && (node[0] == '\0')) node = NULL;
if ((serv != NULL) && (serv[0] == '\0')) serv = NULL;
if ((node == NULL) && (serv == NULL))
{
if (err != NULL) *err = SI_STATUS_EAI_NONAME;
return NULL;
}
switch (family)
{
case AF_UNSPEC:
case AF_INET:
case AF_INET6:
break;
default:
if (err != NULL) *err = SI_STATUS_EAI_FAMILY;
return NULL;
}
switch (socktype)
{
case SOCK_UNSPEC:
case SOCK_RAW:
case SOCK_DGRAM:
case SOCK_STREAM:
break;
default:
if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
return NULL;
}
switch (proto)
{
case IPPROTO_UNSPEC:
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
break;
default:
if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
return NULL;
}
if (((socktype == SOCK_DGRAM) && (proto != IPPROTO_UNSPEC) && (proto != IPPROTO_UDP)) || ((socktype == SOCK_STREAM) && (proto != IPPROTO_UNSPEC) && (proto != IPPROTO_TCP)))
{
if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
return NULL;
}
memset(&a4, 0, sizeof(struct in_addr));
memset(&a6, 0, sizeof(struct in6_addr));
if ((proto == IPPROTO_UNSPEC) && (socktype == SOCK_DGRAM)) proto = IPPROTO_UDP;
if ((proto == IPPROTO_UNSPEC) && (socktype == SOCK_STREAM)) proto = IPPROTO_TCP;
port = 0;
if ((flags & AI_SRV) != 0)
{
out = _gai_srv(si, node, serv, family, socktype, proto, flags, interface, err);
return _gai_sort_list(out);
}
else
{
numericserv = _gai_numericserv(serv, &port);
}
if ((flags & AI_NUMERICSERV) && (numericserv == 0))
{
if (err != NULL) *err = SI_STATUS_EAI_NONAME;
return NULL;
}
if (serv != NULL)
{
if (numericserv == 1)
{
flags |= AI_NUMERICSERV;
servptr = &port;
}
else
{
servptr = serv;
}
}
numerichost = _gai_numerichost(node, &family, flags, &a4, &a6);
if ((numerichost == -1) || ((flags & AI_NUMERICHOST) && (numerichost == 0)))
{
if (err != NULL) *err = SI_STATUS_EAI_NONAME;
return NULL;
}
if (numerichost == 1)
{
flags |= AI_NUMERICHOST;
if (family == AF_INET)
{
nodeptr = &a4;
}
else if (family == AF_INET6)
{
nodeptr = &a6;
}
}
else
{
nodeptr = node;
}
if ((numerichost == 1) && (numericserv == 0))
{
if (_gai_serv_to_port(serv, proto, &port) != 0)
{
if (err != NULL) *err = SI_STATUS_EAI_NONAME;
return NULL;
}
else
{
flags |= AI_NUMERICSERV;
servptr = &port;
numericserv = 1;
}
}
if ((numerichost == 1) && (numericserv == 1))
{
p4 = &a4;
p6 = &a6;
cname = NULL;
if (family == AF_INET) p6 = NULL;
if (family == AF_INET6) p4 = NULL;
if (node == NULL) cname = "localhost";
out = si_addrinfo_list(si, socktype, proto, p4, p6, port, 0, cname, cname);
return _gai_sort_list(out);
}
else if ((si->sim_wants_addrinfo != NULL) && si->sim_wants_addrinfo(si))
{
out = si->sim_addrinfo(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
return _gai_sort_list(out);
}
out = _gai_simple(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
return _gai_sort_list(out);
}
static struct addrinfo *
si_item_to_addrinfo(si_item_t *item)
{
si_addrinfo_t *a;
struct addrinfo *out;
if (item == NULL) return NULL;
a = (si_addrinfo_t *)((uintptr_t)item + sizeof(si_item_t));
out = (struct addrinfo *)calloc(1, sizeof(struct addrinfo));
if (out == NULL) return NULL;
out->ai_flags = a->ai_flags;
out->ai_family = a->ai_family;
out->ai_socktype = a->ai_socktype;
out->ai_protocol = a->ai_protocol;
out->ai_addrlen = a->ai_addrlen;
out->ai_addr = (struct sockaddr *)calloc(1, out->ai_addrlen);
if (out->ai_addr == NULL)
{
free(out);
return NULL;
}
memcpy(out->ai_addr, a->ai_addr.x, out->ai_addrlen);
if (a->ai_canonname != NULL)
{
out->ai_canonname = strdup(a->ai_canonname);
if (out->ai_canonname == NULL)
{
free(out);
return NULL;
}
}
return out;
}
__private_extern__ struct addrinfo *
si_list_to_addrinfo(si_list_t *list)
{
struct addrinfo *tail, *out;
int i;
if (list == NULL) return NULL;
if (list->count == 0) return NULL;
i = list->count - 1;
out = si_item_to_addrinfo(list->entry[i]);
tail = out;
for (i--; i >= 0; i--)
{
out = si_item_to_addrinfo(list->entry[i]);
if (out == NULL)
{
freeaddrinfo(tail);
return NULL;
}
out->ai_next = tail;
tail = out;
}
return out;
}
static si_item_t *
make_hostent(si_mod_t *si, const char *name, struct in_addr addr)
{
char *addrs[2];
char *aliases[1];
uint64_t unused;
if (name == NULL) return NULL;
unused = 0;
addrs[0] = (char *)&(addr.s_addr);
addrs[1] = NULL;
aliases[0] = NULL;
return (si_item_t *)LI_ils_create("L4488s*44a", (unsigned long)si, CATEGORY_HOST_IPV4, 1, unused, unused, name, aliases, AF_INET, IPV4_ADDR_LEN, addrs);
}
static si_item_t *
make_hostent6(si_mod_t *si, const char *name, struct in6_addr addr)
{
char *addrs[2];
char *aliases[1];
uint64_t unused;
if (name == NULL) return NULL;
unused = 0;
addrs[0] = (char *)&(addr.__u6_addr.__u6_addr32[0]);
addrs[1] = NULL;
aliases[0] = NULL;
return (si_item_t *)LI_ils_create("L4488s*44c", (unsigned long)si, CATEGORY_HOST_IPV6, 1, unused, unused, name, aliases, AF_INET6, IPV6_ADDR_LEN, addrs);
}
static char *
lower_case(const char *s)
{
int i;
char *t;
if (s == NULL) return NULL;
t = malloc(strlen(s) + 1);
for (i = 0; s[i] != '\0'; i++)
{
if ((s[i] >= 'A') && (s[i] <= 'Z')) t[i] = s[i] + 32;
else t[i] = s[i];
}
t[i] = '\0';
return t;
}
static int
merge_alias(const char *name, build_hostent_t *h)
{
int i;
if (name == NULL) return 0;
if (h == NULL) return 0;
if ((h->host.h_name != NULL) && (string_equal(name, h->host.h_name))) return 0;
for (i = 0; i < h->alias_count; i++)
{
if (string_equal(name, h->host.h_aliases[i])) return 0;
}
if (h->alias_count == 0) h->host.h_aliases = (char **)calloc(2, sizeof(char *));
else h->host.h_aliases = (char **)reallocf(h->host.h_aliases, (h->alias_count + 2) * sizeof(char *));
if (h->host.h_aliases == NULL)
{
h->alias_count = 0;
return -1;
}
h->host.h_aliases[h->alias_count] = lower_case(name);
h->alias_count++;
h->host.h_aliases[h->alias_count] = NULL;
return 0;
}
static int
append_addr(const char *addr, uint32_t len, build_hostent_t *h)
{
if (addr == NULL) return 0;
if (h == NULL) return 0;
if (h->addr_count == 0) h->host.h_addr_list = (char **)calloc(2, sizeof(char *));
else h->host.h_addr_list = (char **)reallocf(h->host.h_addr_list, (h->addr_count + 2) * sizeof(char *));
if (h->host.h_addr_list == NULL)
{
h->addr_count = 0;
return -1;
}
h->host.h_addr_list[h->addr_count] = malloc(len);
if (h->host.h_addr_list[h->addr_count] == NULL) return -1;
memcpy(h->host.h_addr_list[h->addr_count], addr, len);
h->addr_count++;
h->host.h_addr_list[h->addr_count] = NULL;
return 0;
}
static void
free_build_hostent(build_hostent_t *h)
{
uint32_t i;
char **aliases;
if (h == NULL) return;
if (h->host.h_name != NULL) free(h->host.h_name);
h->host.h_name = NULL;
aliases = h->host.h_aliases;
if (aliases != NULL)
{
while (*aliases != NULL) free(*aliases++);
free(h->host.h_aliases);
}
h->host.h_aliases = NULL;
if (h->host.h_addr_list != NULL)
{
for (i = 0; h->host.h_addr_list[i] != NULL; i++) free(h->host.h_addr_list[i]);
free(h->host.h_addr_list);
}
h->host.h_addr_list = NULL;
free(h);
}
__private_extern__ si_item_t *
si_ipnode_byname(si_mod_t *si, const char *name, int family, int flags, const char *interface, uint32_t *err)
{
int i, status, want;
struct in_addr addr4;
struct in6_addr addr6;
si_item_t *item4, *item6;
build_hostent_t *out;
struct hostent *h;
uint64_t unused;
config_stats_t ifstats;
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)
{
item4 = make_hostent(si, name, addr4);
if (item4 == NULL)
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
return item4;
}
}
else if (family == AF_INET6)
{
status = inet_pton(family, name, &addr6);
if (status == 1)
{
item6 = make_hostent6(si, name, addr6);
if (item6 == NULL)
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
return item6;
}
status = inet_aton(name, &addr4);
if (status == 1)
{
if (!(flags & (AI_V4MAPPED | AI_V4MAPPED_CFG)))
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_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);
item6 = make_hostent6(si, name, addr6);
if (item6 == NULL)
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
return item6;
}
}
else
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
memset(&ifstats, 0, sizeof(config_stats_t));
if (flags & AI_ADDRCONFIG)
{
if (_si_netconfig(&ifstats) < 0)
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
if ((ifstats.v4_count == 0) && (ifstats.v6_count == 0))
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
}
want = WANT_A4_ONLY;
if (family == AF_INET)
{
if ((flags & AI_ADDRCONFIG) && (ifstats.v4_count == 0))
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
}
else if (family == AF_INET6)
{
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) && (ifstats.v6_count == 0))
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
}
}
else
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
item6 = NULL;
item4 = NULL;
if ((want == WANT_A6_ONLY) || (want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) || (want == WANT_A6_PLUS_MAPPED_A4))
{
item6 = si_host_byname(si, name, AF_INET6, interface, (uint32_t *)err);
}
if ((want == WANT_A4_ONLY) || (want == WANT_A6_PLUS_MAPPED_A4) || ((want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) && (item6 == NULL)))
{
item4 = si_host_byname(si, name, AF_INET, interface, (uint32_t *)err);
}
if (want == WANT_A4_ONLY)
{
si_item_release(item6);
if ((item4 == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
return item4;
}
if (want == WANT_A6_ONLY)
{
si_item_release(item4);
if ((item6 == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
return item6;
}
if ((item6 == NULL) && (item4 == NULL))
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
return NULL;
}
out = (build_hostent_t *)calloc(1, sizeof(build_hostent_t));
if (out == NULL)
{
si_item_release(item4);
si_item_release(item6);
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
if (item4 != NULL)
{
h = (struct hostent *)((uintptr_t)item4 + sizeof(si_item_t));
out->host.h_name = lower_case(h->h_name);
if (h->h_aliases != NULL)
{
for (i = 0; h->h_aliases[i] != NULL; i++) merge_alias(h->h_aliases[i], out);
}
for (i = 0; h->h_addr_list[i] != 0; i++)
{
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]), h->h_addr_list[i], IPV4_ADDR_LEN);
append_addr((const char *)&addr6, IPV6_ADDR_LEN, out);
}
}
if (item6 != NULL)
{
h = (struct hostent *)((uintptr_t)item6 + sizeof(si_item_t));
if (out->host.h_name == NULL) out->host.h_name = lower_case(h->h_name);
if (h->h_aliases != NULL)
{
for (i = 0; h->h_aliases[i] != NULL; i++) merge_alias(h->h_aliases[i], out);
}
for (i = 0; h->h_addr_list[i] != 0; i++) append_addr(h->h_addr_list[i], IPV6_ADDR_LEN, out);
}
si_item_release(item4);
si_item_release(item6);
unused = 0;
item6 = (si_item_t *)LI_ils_create("L4488s*44c", (unsigned long)si, CATEGORY_HOST_IPV6, 1, unused, unused, out->host.h_name, out->host.h_aliases, AF_INET6, IPV6_ADDR_LEN, out->host.h_addr_list);
free_build_hostent(out);
return item6;
}