#include "jabberd.h"
#include <netinet/in.h>
#define BIND_8_COMPAT
#include <arpa/nameser.h>
#include <resolv.h>
#include "srv_resolv.h"
#ifndef T_SRV
#define T_SRV 33
#endif
typedef struct __srv_list
{
int priority;
char* port;
char* host;
struct __srv_list* next;
} *srv_list, _srv_list;
char* srv_inet_ntoa(pool p, unsigned char* addrptr)
{
char result[16];
result[15] = '\0';
snprintf(result, 16, "%d.%d.%d.%d", addrptr[0],addrptr[1],addrptr[2],addrptr[3]);
return pstrdup(p, result);
}
#ifdef WITH_IPV6
char* srv_inet_ntop(pool p, const unsigned char* addrptr, int af)
{
char result[INET6_ADDRSTRLEN];
inet_ntop(af, addrptr, result, sizeof(result));
return pstrdup(p, result);
}
#endif
char* srv_port2str(pool p, unsigned short port)
{
char* result = pmalloco(p, 6);
snprintf(result, 6, "%d", port);
return result;
}
char* srv_lookup(pool p, const char* service, const char* domain)
{
unsigned char reply[1024];
int replylen = 0;
char host[1024];
register HEADER* rheader;
unsigned char* rrptr;
int exprc;
int rrtype;
long rrpayloadsz;
srv_list svrlist = NULL;
srv_list tempnode = NULL;
srv_list iternode = NULL;
HASHTABLE arr_table;
spool result;
char* ipname;
char* ipaddr;
#ifdef WITH_IPV6
int error_code;
struct addrinfo hints;
struct addrinfo* addr_res;
#else
struct hostent* hp;
#endif
if (service == NULL)
{
log_debug(ZONE, "srv: Standard resolution of %s", domain);
#ifdef WITH_IPV6
hints.ai_flags = 0;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
error_code = getaddrinfo(domain, NULL, &hints, &addr_res);
if (error_code)
{
log_debug(ZONE, "srv: Error while resolving %s: %s", domain, gai_strerror(error_code));
return NULL;
}
switch (addr_res->ai_family)
{
case PF_INET:
ipaddr = pstrdup(p, srv_inet_ntop(p, (char *)&((struct sockaddr_in*)addr_res->ai_addr)->sin_addr, addr_res->ai_family));
break;
case PF_INET6:
ipaddr = pstrdup(p, srv_inet_ntop(p, (char *)&((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr, addr_res->ai_family));
break;
default:
ipaddr = NULL;
}
freeaddrinfo(addr_res);
log_debug(ZONE, "srv: Resolved %s to: %s", domain, ipaddr);
return ipaddr;
#else
hp = gethostbyname(domain);
if (!hp)
{
log_debug(ZONE, "srv: Unable to resolve: %s", domain);
return NULL;
}
else
{
return pstrdup(p, srv_inet_ntoa(p, hp->h_addr));
}
#endif
}
log_debug(ZONE, "srv: SRV resolution of %s.%s", service, domain);
arr_table = ghash_create(11, (KEYHASHFUNC)str_hash_code, (KEYCOMPAREFUNC)j_strcmp);
if (((_res.options & RES_INIT) == 0) && (res_init() == -1))
{
log_debug(ZONE, "srv: initialization failed on res_init.");
return NULL;
}
replylen = res_querydomain(service, domain,
C_IN,
T_SRV,
(unsigned char*)&reply,
sizeof(reply));
rheader = (HEADER*)reply;
if ( (replylen > 0) && (ntohs(rheader->rcode) == NOERROR) && (ntohs(rheader->ancount) > 0) )
{
exprc = dn_expand(reply,
reply + replylen,
reply + sizeof(HEADER),
host, sizeof(host));
if (exprc < 0)
{
log_debug(ZONE, "srv: DN expansion failed for Question section.");
return NULL;
}
rrptr = reply + sizeof(HEADER) + exprc + 4;
while (rrptr < (reply + replylen))
{
exprc = dn_expand(reply, reply + replylen, rrptr, host, sizeof(host));
if (exprc < 0)
{
log_debug(ZONE, "srv: Whoa nelly! DN expansion failed for RR.");
return NULL;
}
rrptr += exprc;
rrtype = (rrptr[0] << 8 | rrptr[1]);
rrpayloadsz = (rrptr[8] << 8 | rrptr[9]);
rrptr += 10;
switch(rrtype)
{
#ifdef WITH_IPV6
case T_AAAA:
ipaddr = srv_inet_ntop(p, rrptr, AF_INET6);
ipname = pstrdup(p, host);
ghash_put(arr_table, ipname, ipaddr);
break;
#endif
case T_A:
ipaddr = srv_inet_ntoa(p, rrptr);
ipname = pstrdup(p, host);
ghash_put(arr_table, ipname, ipaddr);
break;
case T_SRV:
exprc = dn_expand(reply, reply + replylen, rrptr + 6, host, sizeof(host));
if (exprc < 0)
{
log_debug(ZONE, "srv: DN expansion failed for SRV.");
return NULL;
}
tempnode = pmalloco(p, sizeof(_srv_list));
tempnode->priority = (rrptr[0] << 8 | rrptr[1]);
tempnode->port = srv_port2str(p, (rrptr[4] << 8 | rrptr[5]));
tempnode->host = pstrdup(p, host);
iternode = svrlist;
if (iternode == NULL)
svrlist = tempnode;
else
{
while ( (tempnode->priority > iternode->priority) && (iternode->next != NULL) )
iternode = iternode->next;
if (iternode == svrlist)
{
tempnode->next = svrlist;
svrlist = tempnode;
}
else
{
tempnode->next = iternode->next;
iternode->next = tempnode;
}
}
}
rrptr += rrpayloadsz;
}
result = spool_new(p);
iternode = svrlist;
while (iternode != NULL)
{
if (result->len != 0)
spool_add(result, ",");
ipaddr = (char*)ghash_get(arr_table, iternode->host);
if (ipaddr != NULL)
{
#ifdef WITH_IPV6
if (strchr(ipaddr, ':')) {
spooler(result, "[", ipaddr, "]:", iternode->port, result);
}
else
{
#endif
spooler(result, ipaddr, ":", iternode->port, result);
#ifdef WITH_IPV6
}
#endif
}
else
{
#ifdef WITH_IPV6
log_debug(ZONE, "srv: attempting A / AAAA record lookup.");
hints.ai_flags = 0;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
error_code = getaddrinfo(iternode->host, NULL, &hints, &addr_res);
if (error_code)
{
log_debug(ZONE, "srv: Error while resolving %s: %s", domain, gai_strerror(error_code));
}
else
{
switch (addr_res->ai_family)
{
case PF_INET:
spooler(result,
srv_inet_ntop(p, (char *)&((struct sockaddr_in*)addr_res->ai_addr)->sin_addr, addr_res->ai_family),
":", iternode->port, result);
break;
case PF_INET6:
spooler(result, "[",
srv_inet_ntop(p, (char *)&((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr, addr_res->ai_family),
"]:", iternode->port, result);
break;
default:
ipaddr = NULL;
}
freeaddrinfo(addr_res);
}
#else
log_debug(ZONE, "srv: attempting A record lookup.");
hp = gethostbyname(iternode->host);
if (!hp)
{
log_debug(ZONE, "srv: Unable to resolve SRV reference to: %s\n", iternode->host);
}
else
{
spooler(result, srv_inet_ntoa(p, hp->h_addr), ":", iternode->port, result);
}
#endif
}
iternode = iternode->next;
}
return spool_print(result);
}
return NULL;
}