#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include <nameser.h>
#include <resolv.h>
#define SOCK_UNSPEC 0
#define IPPROTO_UNSPEC 0
#define LONG_STRING_LENGTH 8192
#define _LU_MAXLUSTRLEN 256
static char *LOOKUPD_NAME = "lookup daemon";
extern int _lookup_link();
extern int _lookup_one();
extern int _lookup_all();
struct lu_dict
{
int count;
char *type;
char *name;
char *cname;
char *mx;
char *ipv4;
char *ipv6;
char *service;
char *port;
char *protocol;
char *target;
char *priority;
char *weight;
struct lu_dict *lu_next;
};
#define LU_NAME 1
#define LU_CNAME 2
#define LU_MX 3
#define LU_IPV4 4
#define LU_IPV6 5
#define LU_PORT 6
#define LU_TARGET 7
#define LU_PRIORITY 8
#define LU_WEIGHT 9
static int supported_family[] =
{
PF_UNSPEC,
PF_INET,
PF_INET6
};
static int supported_family_count = 3;
static int supported_socket[] =
{
SOCK_UNSPEC,
SOCK_DGRAM,
SOCK_STREAM
};
static int supported_socket_count = 3;
static int supported_protocol[] =
{
IPPROTO_UNSPEC,
IPPROTO_UDP,
IPPROTO_TCP
};
static int supported_protocol_count = 3;
static int supported_socket_protocol_pair[] =
{
SOCK_UNSPEC, IPPROTO_UNSPEC,
SOCK_UNSPEC, IPPROTO_UDP,
SOCK_UNSPEC, IPPROTO_TCP,
SOCK_DGRAM, IPPROTO_UNSPEC,
SOCK_DGRAM, IPPROTO_UDP,
SOCK_STREAM, IPPROTO_UNSPEC,
SOCK_STREAM, IPPROTO_TCP
};
static int supported_socket_protocol_pair_count = 7;
static int
gai_family_type_check(int f)
{
int i;
for (i = 0; i < supported_family_count; i++)
{
if (f == supported_family[i]) return 0;
}
return 1;
}
static int
gai_socket_type_check(int s)
{
int i;
for (i = 0; i < supported_socket_count; i++)
{
if (s == supported_socket[i]) return 0;
}
return 1;
}
static int
gai_protocol_type_check(int p)
{
int i;
for (i = 0; i < supported_protocol_count; i++)
{
if (p == supported_protocol[i]) return 0;
}
return 1;
}
static int
gai_socket_protocol_type_check(int s, int p)
{
int i, j, ss, sp;
for (i = 0, j = 0; i < supported_socket_protocol_pair_count; i++, j+=2)
{
ss = supported_socket_protocol_pair[j];
sp = supported_socket_protocol_pair[j+1];
if ((s == ss) && (p == sp)) return 0;
}
return 1;
}
static int
gai_inet_pton(const char *s, struct in6_addr *a6)
{
int run, ncolon;
unsigned short x[8];
char *p, buf[4];
if (s == NULL) return 0;
if (a6 == NULL) return 0;
for (ncolon = 0; ncolon < 8; ncolon++) x[ncolon] = 0;
memset(buf, 0, 4);
ncolon = 0;
run = 0;
for (p = (char *)s; *p != '\0'; p++)
{
if (*p == ':')
{
if (run > 0) sscanf(buf, "%hx", &(x[ncolon]));
ncolon++;
if (ncolon > 7) return 0;
run = 0;
memset(buf, 0, 4);
}
else if (((*p >= '0') && (*p <= '9')) || ((*p >= 'a') && (*p <= 'f')) || ((*p >= 'A') && (*p <= 'F')))
{
buf[run] = *p;
run++;
if (run > 4) return 0;
}
}
if (ncolon != 7) return 0;
if (run > 0) sscanf(buf, "%hx", &(x[7]));
a6->__u6_addr.__u6_addr32[0] = (x[0] << 16) + x[1];
a6->__u6_addr.__u6_addr32[1] = (x[2] << 16) + x[3];
a6->__u6_addr.__u6_addr32[2] = (x[4] << 16) + x[5];
a6->__u6_addr.__u6_addr32[3] = (x[6] << 16) + x[7];
return 1;
}
static char *
gai_inet_ntop(struct in6_addr a)
{
static char buf[128];
char t[32];
unsigned short x;
char *p;
int i;
memset(buf, 0, 128);
p = (char *)&a.__u6_addr.__u6_addr32;
for (i = 0; i < 8; i++, x += 1)
{
memmove(&x, p, 2);
p += 2;
sprintf(t, "%hx", x);
strcat(buf, t);
if (i < 7) strcat(buf, ":");
}
return buf;
}
char *
gai_strerror(int 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";
}
return "Unknown error";
}
static int
is_ipv4_address(const char *s)
{
struct in_addr a;
if (s == NULL) return 0;
return inet_aton(s, &a);
}
static int
is_ipv6_address(const char *s)
{
struct in6_addr a;
if (s == NULL) return 0;
return gai_inet_pton(s, &a);
}
static void
append_addrinfo(struct addrinfo **l, struct addrinfo *a)
{
struct addrinfo *x;
if (l == NULL) return;
if (a == NULL) return;
if (*l == NULL)
{
*l = a;
return;
}
x = *l;
while (x->ai_next != NULL) x = x->ai_next;
x->ai_next = a;
}
static void
free_lu_dict(struct lu_dict *d)
{
struct lu_dict *next;
while (d != NULL)
{
next = d->lu_next;
if (d->type != NULL) free(d->type);
if (d->name != NULL) free(d->name);
if (d->cname != NULL) free(d->cname);
if (d->mx != NULL) free(d->mx);
if (d->ipv4 != NULL) free(d->ipv4);
if (d->ipv6 != NULL) free(d->ipv6);
if (d->service != NULL) free(d->service);
if (d->port != NULL) free(d->port);
if (d->protocol != NULL) free(d->protocol);
if (d->target != NULL) free(d->target);
if (d->priority != NULL) free(d->priority);
if (d->weight != NULL) free(d->weight);
free(d);
d = next;
}
}
static void
append_lu_dict(struct lu_dict **l, struct lu_dict *d)
{
struct lu_dict *x;
if (l == NULL) return;
if (d == NULL) return;
if (*l == NULL)
{
*l = d;
return;
}
x = *l;
while (x->lu_next != NULL) x = x->lu_next;
x->lu_next = d;
}
static void
lookupd_process_dictionary(XDR *inxdr, struct lu_dict **l)
{
int i, nkeys, j, nvals;
int addme;
char *key, *val;
struct lu_dict *d;
if (!xdr_int(inxdr, &nkeys)) return;
d = (struct lu_dict *)malloc(sizeof(struct lu_dict));
memset(d, 0, sizeof(struct lu_dict));
for (i = 0; i < nkeys; i++)
{
key = NULL;
if (!xdr_string(inxdr, &key, LONG_STRING_LENGTH))
{
free_lu_dict(d);
return;
}
addme = 0;
if (!strcmp(key, "name")) addme = LU_NAME;
else if (!strcmp(key, "cname")) addme = LU_CNAME;
else if (!strcmp(key, "mail_exchanger")) addme = LU_MX;
else if (!strcmp(key, "ip_address")) addme = LU_IPV4;
else if (!strcmp(key, "ipv6_address")) addme = LU_IPV6;
else if (!strcmp(key, "port")) addme = LU_PORT;
else if (!strcmp(key, "target")) addme = LU_TARGET;
else if (!strcmp(key, "priority")) addme = LU_PRIORITY;
else if (!strcmp(key, "preference")) addme = LU_PRIORITY;
else if (!strcmp(key, "weight")) addme = LU_WEIGHT;
free(key);
if (!xdr_int(inxdr, &nvals))
{
free_lu_dict(d);
return;
}
for (j = 0; j < nvals; j++)
{
val = NULL;
if (!xdr_string(inxdr, &val, LONG_STRING_LENGTH))
{
free_lu_dict(d);
return;
}
if (addme == 0) free(val);
else if (addme == LU_NAME) d->name = val;
else if (addme == LU_CNAME) d->cname = val;
else if (addme == LU_MX) d->mx = val;
else if (addme == LU_IPV4) d->ipv4 = val;
else if (addme == LU_IPV6) d->ipv6 = val;
else if (addme == LU_PORT) d->port = val;
else if (addme == LU_TARGET) d->target = val;
else if (addme == LU_PRIORITY) d->priority = val;
else if (addme == LU_WEIGHT) d->weight = val;
if (addme != 0) d->count++;
addme = 0;
}
}
append_lu_dict(l, d);
}
static mach_port_t
lookupd_port(char *name)
{
mach_port_t p;
kern_return_t status;
status = bootstrap_look_up(bootstrap_port, name, &p);
if (status == KERN_SUCCESS) return p;
return MACH_PORT_NULL;
}
static int
encode_kv(XDR *x, char *k, char *v)
{
int n = 1;
if (!xdr_string(x, &k, _LU_MAXLUSTRLEN)) return 1;
if (!xdr_int(x, &n)) return 1;
if (!xdr_string(x, &v, _LU_MAXLUSTRLEN)) return 1;
return 0;
}
static int
gai_files(struct lu_dict *q, struct lu_dict **list)
{
int port, i;
struct servent *s;
struct hostent *h;
struct lu_dict *d;
char str[64];
struct in_addr a4;
if (!strcmp(q->type, "service"))
{
s = NULL;
if (q->name != NULL)
{
s = getservbyname(q->name, q->protocol);
}
else if (q->port != NULL)
{
port = atoi(q->port);
s = getservbyport(port, q->protocol);
}
if (s == NULL) return 0;
d = (struct lu_dict *)malloc(sizeof(struct lu_dict));
memset(d, 0, sizeof(struct lu_dict));
if (s->s_name != NULL) d->name = strdup(s->s_name);
sprintf(str, "%u", ntohl(s->s_port));
d->port = strdup(str);
if (s->s_proto != NULL) d->protocol = strdup(s->s_proto);
append_lu_dict(list, d);
return 1;
}
else if (!strcmp(q->type, "host"))
{
h = NULL;
if (q->name != NULL)
{
h = gethostbyname(q->name);
}
else if (q->ipv4 != NULL)
{
if (inet_aton(q->ipv4, &a4) == 0) return -1;
h = gethostbyaddr((char *)&a4, sizeof(struct in_addr), PF_INET);
}
else
{
return 0;
}
if (h == NULL) return 0;
for (i = 0; h->h_addr_list[i] != 0; i++)
{
d = (struct lu_dict *)malloc(sizeof(struct lu_dict));
memset(d, 0, sizeof(struct lu_dict));
if (h->h_name != NULL) d->name = strdup(h->h_name);
memmove((void *)&a4.s_addr, h->h_addr_list[i], h->h_length);
sprintf(str, "%s", inet_ntoa(a4));
d->ipv4 = strdup(str);
append_lu_dict(list, d);
}
return i;
}
return 0;
}
static int
gai_lookupd(struct lu_dict *q, struct lu_dict **list)
{
unsigned datalen;
XDR outxdr;
XDR inxdr;
int proc;
char *listbuf;
char databuf[_LU_MAXLUSTRLEN * BYTES_PER_XDR_UNIT];
int n, i, na;
kern_return_t status;
mach_port_t server_port;
if (q == NULL) return 0;
if (q->count == 0) return 0;
if (q->type == NULL) return 0;
if (list == NULL) return -1;
server_port = lookupd_port(LOOKUPD_NAME);
if (server_port == NULL) return gai_files(q, list);
status = _lookup_link(server_port, "query", &proc);
if (status != KERN_SUCCESS) return gai_files(q, list);
xdrmem_create(&outxdr, databuf, sizeof(databuf), XDR_ENCODE);
na = q->count;
if (!xdr_int(&outxdr, &na))
{
xdr_destroy(&outxdr);
return -1;
}
if (encode_kv(&outxdr, "_lookup_category", q->type) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
if (q->name != NULL)
{
if (encode_kv(&outxdr, "name", q->name) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
}
if (q->ipv4 != NULL)
{
if (encode_kv(&outxdr, "ip_address", q->ipv4) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
}
if (q->ipv6 != NULL)
{
if (encode_kv(&outxdr, "ipv6_address", q->ipv6) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
}
if (q->service != NULL)
{
if (encode_kv(&outxdr, "service", q->service) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
}
if (q->port != NULL)
{
if (encode_kv(&outxdr, "port", q->port) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
}
if (q->protocol != NULL)
{
if (encode_kv(&outxdr, "protocol", q->protocol) != 0)
{
xdr_destroy(&outxdr);
return -1;
}
}
listbuf = NULL;
datalen = 0;
n = xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT;
status = _lookup_all(server_port, proc, databuf, n, &listbuf, &datalen);
if (status != KERN_SUCCESS)
{
xdr_destroy(&outxdr);
return -1;
}
xdr_destroy(&outxdr);
#ifdef NOTDEF
datalen *= BYTES_PER_XDR_UNIT;
#endif
xdrmem_create(&inxdr, listbuf, datalen, XDR_DECODE);
if (!xdr_int(&inxdr, &n))
{
xdr_destroy(&inxdr);
return -1;
}
for (i = 0; i < n; i++)
{
lookupd_process_dictionary(&inxdr, list);
}
xdr_destroy(&inxdr);
vm_deallocate(mach_task_self(), (vm_address_t)listbuf, datalen);
return n;
}
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;
}
}
static struct addrinfo *
new_addrinfo_v4(int flags, int sock, int proto, unsigned short port, struct in_addr addr, char *cname)
{
struct addrinfo *a;
struct sockaddr_in *sa;
int len;
a = (struct addrinfo *)malloc(sizeof(struct addrinfo));
memset(a, 0, sizeof(struct addrinfo));
a->ai_next = NULL;
a->ai_flags = flags;
a->ai_family = PF_INET;
a->ai_socktype = sock;
a->ai_protocol = proto;
a->ai_addrlen = sizeof(struct sockaddr_in);
sa = (struct sockaddr_in *)malloc(a->ai_addrlen);
memset(sa, 0, a->ai_addrlen);
sa->sin_len = a->ai_addrlen;
sa->sin_family = PF_INET;
sa->sin_port = port;
sa->sin_addr = addr;
a->ai_addr = (struct sockaddr *)sa;
if (cname != NULL)
{
len = strlen(cname) + 1;
a->ai_canonname = malloc(len);
memmove(a->ai_canonname, cname, len);
}
return a;
}
static struct addrinfo *
new_addrinfo_v6(int flags, int sock, int proto, unsigned short port, struct in6_addr addr, char *cname)
{
struct addrinfo *a;
struct sockaddr_in6 *sa;
int len;
a = (struct addrinfo *)malloc(sizeof(struct addrinfo));
memset(a, 0, sizeof(struct addrinfo));
a->ai_next = NULL;
a->ai_flags = flags;
a->ai_family = PF_INET6;
a->ai_socktype = sock;
a->ai_protocol = proto;
a->ai_addrlen = sizeof(struct sockaddr_in6);
sa = (struct sockaddr_in6 *)malloc(a->ai_addrlen);
memset(sa, 0, a->ai_addrlen);
sa->sin6_len = a->ai_addrlen;
sa->sin6_family = PF_INET6;
sa->sin6_port = port;
sa->sin6_addr = addr;
a->ai_addr = (struct sockaddr *)sa;
if (cname != NULL)
{
len = strlen(cname) + 1;
a->ai_canonname = malloc(len);
memmove(a->ai_canonname, cname, len);
}
return a;
}
static void
grok_nodename(const char *nodename, int family, struct lu_dict *q)
{
if (nodename == NULL) return;
if (q == NULL) return;
if (((family == PF_UNSPEC) || (family == PF_INET)) && is_ipv4_address(nodename))
{
q->ipv4 = (char *)nodename;
}
else if (((family == PF_UNSPEC) || (family == PF_INET6)) && is_ipv6_address(nodename))
{
q->ipv6 = (char *)nodename;
}
else
{
q->name = (char *)nodename;
}
}
static void
grok_service(const char *servname, struct lu_dict *q)
{
int port;
char *p;
if (servname == NULL) return;
if (q == NULL) return;
port = 0;
for (p = (char *)servname; (port == 0) && (*p != '\0'); p++)
{
if (!isdigit(*p)) port = -1;
}
if (port == 0) port = atoi(servname);
if ((port > 0) && (port < 0xffff)) q->port = (char *)servname;
else q->service = (char *)servname;
}
static int
gai_numerichost(struct lu_dict *h, struct lu_dict **list)
{
struct lu_dict *a;
int n;
if (h == NULL) return 0;
if (list == NULL) return -1;
n = 0;
if (h->ipv4 != NULL)
{
a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
memset(a, 0, sizeof(struct lu_dict));
a->ipv4 = strdup(h->ipv4);
append_lu_dict(list, a);
n++;
}
if (h->ipv6 != NULL)
{
a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
memset(a, 0, sizeof(struct lu_dict));
a->ipv6 = strdup(h->ipv6);
append_lu_dict(list, a);
n++;
}
return n;
}
static int
gai_numericserv(struct lu_dict *s, struct lu_dict **list)
{
struct lu_dict *a;
if (s == NULL) return 0;
if (s->port == NULL) return 0;
if (list == NULL) return -1;
a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
memset(a, 0, sizeof(struct lu_dict));
a->port = strdup(s->port);
append_lu_dict(list, a);
return 1;
}
static void
merge_addr4(struct in_addr a, struct sockaddr_in ***l, int *n)
{
int i;
struct sockaddr_in *sa4;
for (i = 0; i < *n; i++)
{
if (((*l)[i]->sin_family == PF_INET) && (a.s_addr == (*l)[i]->sin_addr.s_addr)) return;
}
sa4 = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
memset(sa4, 0, sizeof(struct sockaddr_in));
sa4->sin_family = PF_INET;
sa4->sin_addr = a;
if (*n == 0) *l = (struct sockaddr_in **)malloc(sizeof(struct sockaddr_in *));
else *l = (struct sockaddr_in **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in *));
(*l)[*n] = sa4;
(*n)++;
}
static void
merge_addr6(struct in6_addr a, struct sockaddr_in6 ***l, int *n)
{
int i;
struct sockaddr_in6 *sa6;
for (i = 0; i < *n; i++)
{
if (((*l)[i]->sin6_family == PF_INET6)
&& (a.__u6_addr.__u6_addr32[0] == (*l)[i]->sin6_addr.__u6_addr.__u6_addr32[0])) return;
}
sa6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
memset(sa6, 0, sizeof(struct sockaddr_in6));
sa6->sin6_family = PF_INET6;
sa6->sin6_addr = a;
if (*n == 0) *l = (struct sockaddr_in6 **)malloc(sizeof(struct sockaddr_in6 *));
else *l = (struct sockaddr_in6 **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in6 *));
(*l)[*n] = sa6;
(*n)++;
}
static void
merge_plist(unsigned short port, unsigned short proto, struct sockaddr_in ***l, int *n)
{
int i;
struct sockaddr_in *sa4;
for (i = 0; i < *n; i++)
{
if ((port == (*l)[i]->sin_port) && (proto == (*l)[i]->sin_family)) return;
}
sa4 = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
memset(sa4, 0, sizeof(struct sockaddr_in));
sa4->sin_port = port;
sa4->sin_family = proto;
if (*n == 0) *l = (struct sockaddr_in **)malloc(sizeof(struct sockaddr_in *));
else *l = (struct sockaddr_in **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in *));
(*l)[*n] = (struct sockaddr_in *)sa4;
(*n)++;
}
static int
gai_random()
{
static int did_init = 0;
static unsigned int randseed = 1;
int x, hi, lo, t;
struct timeval tv;
if (did_init++ == 0)
{
gettimeofday(&tv, NULL);
randseed = tv.tv_usec;
if(randseed == 0) randseed = 1;
}
x = randseed;
hi = x / 127773;
lo = x % 127773;
t = 16807 * lo - 2836 * hi;
if (t <= 0) t += 0x7fffffff;
randseed = t;
return t;
}
void
lu_prioity_sort(struct lu_dict **l)
{
struct lu_dict *d, **nodes;
unsigned int x, count, i, j, bottom, *pri, *wgt, swap, t;
if (*l == NULL) return;
count = 0;
for (d = *l; d != NULL; d = d->lu_next) count++;
nodes = (struct lu_dict **)malloc(count * sizeof(struct lu_dict *));
pri = (unsigned int *)malloc(count * sizeof(unsigned int));
wgt = (unsigned int *)malloc(count * sizeof(unsigned int));
for (i = 0, d = *l; d != NULL; d = d->lu_next, i++)
{
nodes[i] = d;
x = (unsigned int)-1;
if (d->priority != NULL) x = atoi(d->priority);
pri[i] = x;
x = 0;
if (d->weight != NULL) x = atoi(d->weight);
wgt[i] = (gai_random() % 10000) * x;
}
swap = 1;
bottom = count - 1;
while (swap == 1)
{
swap = 0;
for (i = 0, j = 1; i < bottom; i++, j++)
{
if (pri[i] < pri[j]) continue;
if ((pri[i] == pri[j]) && (wgt[i] < wgt[j])) continue;
swap = 1;
t = pri[i];
pri[i] = pri[j];
pri[j] = t;
t = wgt[i];
wgt[i] = wgt[j];
wgt[j] = t;
d = nodes[i];
nodes[i] = nodes[j];
nodes[j] = d;
}
bottom--;
}
*l = nodes[0];
bottom = count - 1;
for (i = 0, j = 1; i < bottom; i++, j++) nodes[i]->lu_next = nodes[j];
nodes[bottom]->lu_next = NULL;
free(pri);
free(wgt);
free(nodes);
}
static void
gai_serv_lookupd(const char *servname, int proto, int numericserv, struct lu_dict **list)
{
struct lu_dict q, *sub;
memset(&q, 0, sizeof(struct lu_dict));
q.count = 3;
q.type = "service";
grok_service(servname, &q);
if (q.service != NULL)
{
q.name = q.service;
q.service = NULL;
}
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
{
sub = NULL;
q.protocol = "udp";
if (numericserv == 1) gai_numericserv(&q, &sub);
else gai_lookupd(&q, &sub);
append_lu_dict(list, sub);
for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("udp");
}
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
{
sub = NULL;
q.protocol = "tcp";
if (numericserv == 1) gai_numericserv(&q, &sub);
else gai_lookupd(&q, &sub);
append_lu_dict(list, sub);
for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("tcp");
}
}
static int
gai_serv(const char *servname, const struct addrinfo *hints, struct addrinfo **res)
{
struct lu_dict *list, *d;
int proto, family, socktype, setcname, wantv4, wantv6;
unsigned short port;
char *loopv4, *loopv6;
struct addrinfo *a;
struct in_addr a4;
struct in6_addr a6;
loopv4 = "127.0.0.1";
loopv6 = "0:0:0:0:0:0:0:1";
family = PF_UNSPEC;
proto = IPPROTO_UNSPEC;
setcname = 0;
if (hints != NULL)
{
proto = hints->ai_protocol;
if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
if (hints->ai_flags & AI_CANONNAME) setcname = 1;
if ((hints->ai_flags & AI_PASSIVE) == 1)
{
loopv4 = "0.0.0.0";
loopv6 = "0:0:0:0:0:0:0:0";
}
family = hints->ai_family;
}
wantv4 = 1;
wantv6 = 1;
if (family == PF_INET6) wantv4 = 0;
if (family == PF_INET) wantv6 = 0;
list = NULL;
gai_serv_lookupd(servname, proto, 0, &list);
if (list == NULL) gai_serv_lookupd(servname, proto, 1, &list);
for (d = list; d != NULL; d = d->lu_next)
{
if ((d->port == NULL) || (d->protocol == NULL)) continue;
port = htons(atoi(d->port));
proto = IPPROTO_UDP;
socktype = SOCK_DGRAM;
if (!strcasecmp(d->protocol, "tcp"))
{
proto = IPPROTO_TCP;
socktype = SOCK_STREAM;
}
if (wantv4 == 1)
{
inet_aton(loopv4, &a4);
a = new_addrinfo_v4(0, socktype, proto, port, a4, NULL);
append_addrinfo(res, a);
}
if (wantv6 == 1)
{
gai_inet_pton(loopv6, &a6);
a = new_addrinfo_v6(0, socktype, proto, port, a6, NULL);
append_addrinfo(res, a);
}
}
free_lu_dict(list);
if ((setcname == 1) && (*res != NULL))
{
if (res[0]->ai_canonname == NULL) res[0]->ai_canonname = strdup("localhost");
}
return 0;
}
static void
gai_node_lookupd(const char *nodename, int family, int numerichost, struct lu_dict **list)
{
struct lu_dict q;
memset(&q, 0, sizeof(struct lu_dict));
q.count = 2;
q.type = "host";
grok_nodename(nodename, family, &q);
if (numerichost) gai_numerichost(&q, list);
else gai_lookupd(&q, list);
}
static int
gai_node(const char *nodename, const struct addrinfo *hints, struct addrinfo **res)
{
int i, family, numerichost, setcname, a_list_count, wantv4, wantv6;
struct lu_dict *list, *d, *t;
char *cname;
struct in_addr a4;
struct in6_addr a6;
struct addrinfo *a;
struct sockaddr **a_list;
struct sockaddr_in *sa4;
struct sockaddr_in6 *sa6;
a_list_count = 0;
a_list = NULL;
numerichost = 0;
family = PF_UNSPEC;
setcname = 0;
cname = NULL;
if (hints != NULL)
{
family = hints->ai_family;
if (hints->ai_flags & AI_NUMERICHOST) numerichost = 1;
if (hints->ai_flags & AI_CANONNAME) setcname = 1;
}
wantv4 = 1;
wantv6 = 1;
if (family == PF_INET6) wantv4 = 0;
if (family == PF_INET) wantv6 = 0;
t = NULL;
gai_node_lookupd(nodename, family, numerichost, &t);
if ((t == NULL) && (numerichost == 0))
{
gai_node_lookupd(nodename, family, 1, &t);
}
list = NULL;
for (d = t; d != NULL; d = d->lu_next)
{
if (d->cname != NULL)
{
if (cname == NULL) cname = strdup(d->cname);
gai_node_lookupd(d->cname, family, 0, &t);
}
}
append_lu_dict(&list, t);
for (d = list; d != NULL; d = d->lu_next)
{
if ((cname == NULL) && (d->name != NULL) && (strchr(d->name, '.') != NULL))
{
cname = strdup(d->name);
}
if ((d->ipv4 != NULL) && (wantv4 == 1))
{
inet_aton(d->ipv4, &a4);
merge_addr4(a4, (struct sockaddr_in ***)&a_list, &a_list_count);
}
if ((d->ipv6 != NULL) && (wantv6 == 1))
{
gai_inet_pton(d->ipv6, &a6);
merge_addr6(a6, (struct sockaddr_in6 ***)&a_list, &a_list_count);
}
}
for (d = list; (cname == NULL) && (d != NULL); d = d->lu_next)
{
if (d->name != NULL) cname = strdup(d->name);
}
free_lu_dict(list);
for (i = 0; i < a_list_count; i++)
{
if (a_list[i]->sa_family == PF_INET)
{
sa4 = (struct sockaddr_in *)a_list[i];
a = new_addrinfo_v4(0, 0, 0, 0, sa4->sin_addr, NULL);
append_addrinfo(res, a);
}
else if (a_list[i]->sa_family == PF_INET6)
{
sa6 = (struct sockaddr_in6 *)a_list[i];
a = new_addrinfo_v6(0, 0, 0, 0, sa6->sin6_addr, NULL);
append_addrinfo(res, a);
}
free(a_list[i]);
}
if (a_list_count > 0) free(a_list);
if ((setcname == 1) && (*res != NULL))
{
if (res[0]->ai_canonname == NULL)
{
res[0]->ai_canonname = cname;
cname = NULL;
}
}
if (cname != NULL) free(cname);
return 0;
}
static void
gai_nodeserv_lookupd(const char *nodename, const char *servname, const struct addrinfo *hints, struct lu_dict **list)
{
struct lu_dict q, *sub;
int proto, family;
family = PF_UNSPEC;
proto = IPPROTO_UNSPEC;
if (hints != NULL)
{
if (hints->ai_flags & AI_NUMERICHOST) return;
family = hints->ai_family;
proto = hints->ai_protocol;
if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
}
memset(&q, 0, sizeof(struct lu_dict));
q.count = 4;
q.type = "host";
grok_nodename(nodename, family, &q);
grok_service(servname, &q);
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
{
sub = NULL;
q.protocol = "udp";
gai_lookupd(&q, &sub);
append_lu_dict(list, sub);
for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("udp");
}
if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
{
sub = NULL;
q.protocol = "tcp";
gai_lookupd(&q, &sub);
append_lu_dict(list, sub);
for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("tcp");
}
}
static void
gai_node_pp(const char *nodename, unsigned short port, int proto, int family, int setcname, struct addrinfo **res)
{
struct lu_dict *list, *d;
int i, wantv4, wantv6, a_list_count, socktype;
char *cname;
struct sockaddr **a_list;
struct in_addr a4;
struct in6_addr a6;
struct sockaddr_in *sa4;
struct sockaddr_in6 *sa6;
struct addrinfo *a;
socktype = SOCK_UNSPEC;
if (proto == IPPROTO_UDP) socktype = SOCK_DGRAM;
if (proto == IPPROTO_TCP) socktype = SOCK_STREAM;
cname = NULL;
wantv4 = 1;
wantv6 = 1;
if (family == PF_INET6) wantv4 = 0;
if (family == PF_INET) wantv6 = 0;
list = NULL;
gai_node_lookupd(nodename, family, 0, &list);
if (list == NULL)
{
gai_node_lookupd(nodename, family, 1, &list);
}
for (d = list; d != NULL; d = d->lu_next)
{
if (d->cname != NULL)
{
cname = strdup(d->cname);
gai_node_lookupd(d->cname, family, 0, &list);
}
}
a_list_count = 0;
for (d = list; d != NULL; d = d->lu_next)
{
if ((cname == NULL) && (d->name != NULL) && (strchr(d->name, '.') != NULL))
{
cname = strdup(d->name);
}
if ((d->ipv4 != NULL) && (wantv4 == 1))
{
inet_aton(d->ipv4, &a4);
merge_addr4(a4, (struct sockaddr_in ***)&a_list, &a_list_count);
}
if ((d->ipv6 != NULL) && (wantv6 == 1))
{
gai_inet_pton(d->ipv6, &a6);
merge_addr6(a6, (struct sockaddr_in6 ***)&a_list, &a_list_count);
}
}
free_lu_dict(list);
for (i = 0; i < a_list_count; i++)
{
if (a_list[i]->sa_family == PF_INET)
{
sa4 = (struct sockaddr_in *)a_list[i];
a = new_addrinfo_v4(0, socktype, proto, port, sa4->sin_addr, NULL);
append_addrinfo(res, a);
}
else if (a_list[i]->sa_family == PF_INET6)
{
sa6 = (struct sockaddr_in6 *)a_list[i];
a = new_addrinfo_v6(0, socktype, proto, port, sa6->sin6_addr, NULL);
append_addrinfo(res, a);
}
free(a_list[i]);
}
if (a_list_count > 0) free(a_list);
if ((setcname == 1) && (*res != NULL))
{
if (res[0]->ai_canonname == NULL)
{
res[0]->ai_canonname = cname;
cname = NULL;
}
}
if (cname != NULL) free(cname);
}
static int
gai_nodeserv(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
{
struct lu_dict *srv_list, *node_list, *s, *n;
int numerichost, family, proto, setcname;
int wantv4, wantv6, i, j, gotmx, p_list_count;
unsigned short port;
char *cname;
struct sockaddr **p_list;
struct sockaddr_in *sa4;
numerichost = 0;
family = PF_UNSPEC;
proto = IPPROTO_UNSPEC;
setcname = 0;
cname = NULL;
wantv4 = 1;
wantv6 = 1;
if (hints != NULL)
{
family = hints->ai_family;
if (hints->ai_flags & AI_NUMERICHOST) numerichost = 1;
if (hints->ai_flags & AI_CANONNAME) setcname = 1;
proto = hints->ai_protocol;
if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
}
if (family == PF_INET6) wantv4 = 0;
if (family == PF_INET) wantv6 = 0;
srv_list = NULL;
gai_nodeserv_lookupd(nodename, servname, hints, &srv_list);
lu_prioity_sort(&srv_list);
if (srv_list != NULL)
{
for (s = srv_list; s != NULL; s = s->lu_next)
{
if (s->port == NULL) continue;
if (s->protocol == NULL) continue;
i = htons(atoi(s->port));
j = IPPROTO_UDP;
if (!strcmp(s->protocol, "tcp")) j = IPPROTO_TCP;
gai_node_pp(s->target, i, j, family, setcname, res);
}
free_lu_dict(srv_list);
return 0;
}
gotmx = 0;
node_list = NULL;
if (!strcmp(servname, "smtp"))
{
gai_node_lookupd(nodename, family, numerichost, &node_list);
if ((node_list == NULL) && (numerichost == 0))
{
gai_node_lookupd(nodename, family, 1, &node_list);
}
lu_prioity_sort(&node_list);
for (n = node_list; (n != NULL) && (gotmx == 0); n = n->lu_next)
{
if (n->mx != NULL) gotmx = 1;
}
if ((gotmx == 0) && (node_list != NULL))
{
free_lu_dict(node_list);
node_list = NULL;
}
}
srv_list = NULL;
gai_serv_lookupd(servname, proto, 0, &srv_list);
if (srv_list == NULL) gai_serv_lookupd(servname, proto, 1, &srv_list);
if (srv_list == NULL) return 0;
p_list_count = 0;
for (s = srv_list; s != NULL; s = s->lu_next)
{
if (s->port == NULL) continue;
if (s->protocol == NULL) continue;
i = htons(atoi(s->port));
j = IPPROTO_UDP;
if (!strcmp(s->protocol, "tcp")) j = IPPROTO_TCP;
merge_plist(i, j, (struct sockaddr_in ***)&p_list, &p_list_count);
}
free_lu_dict(srv_list);
for (i = 0; i < p_list_count; i++)
{
sa4 = (struct sockaddr_in *)p_list[i];
port = sa4->sin_port;
proto = sa4->sin_family;
if (gotmx == 1)
{
for (n = node_list; n != NULL; n = n->lu_next)
{
if (n->mx != NULL) gai_node_pp(n->mx, port, proto, family, setcname, res);
}
}
else
{
gai_node_pp(nodename, port, proto, family, setcname, res);
}
free(p_list[i]);
}
if (node_list != NULL) free_lu_dict(node_list);
if (p_list_count > 0) free(p_list);
return 0;
}
int
getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
{
int status;
struct lu_dict *list;
if (res == NULL) return 0;
*res = NULL;
list = NULL;
if ((nodename == NULL) && (servname == NULL)) return EAI_NONAME;
if (hints != NULL)
{
if (hints->ai_addrlen != 0) return EAI_BADHINTS;
if (hints->ai_canonname != NULL) return EAI_BADHINTS;
if (hints->ai_addr != NULL) return EAI_BADHINTS;
if (hints->ai_next != NULL) return EAI_BADHINTS;
if (gai_family_type_check(hints->ai_family) != 0) return EAI_FAMILY;
if (gai_socket_type_check(hints->ai_socktype) != 0) return EAI_BADHINTS;
if (gai_protocol_type_check(hints->ai_protocol) != 0) return EAI_BADHINTS;
if (gai_socket_protocol_type_check(hints->ai_socktype, hints->ai_protocol) != 0) return EAI_BADHINTS;
}
status = 0;
if (nodename == NULL)
{
return gai_serv(servname, hints, res);
}
if (servname == NULL)
{
return gai_node(nodename, hints, res);
}
return gai_nodeserv(nodename, servname, hints, res);
}