#include "resolver.h"
#include "dns.h"
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#ifdef HAVE_WINDNS_H
# include <windns.h>
#endif
typedef struct {
unsigned id :16;
#if BYTE_ORDER == BIG_ENDIAN
unsigned qr: 1;
unsigned opcode: 4;
unsigned aa: 1;
unsigned tc: 1;
unsigned rd: 1;
unsigned ra: 1;
unsigned unused :3;
unsigned rcode :4;
#endif
#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
unsigned rd :1;
unsigned tc :1;
unsigned aa :1;
unsigned opcode :4;
unsigned qr :1;
unsigned rcode :4;
unsigned unused :3;
unsigned ra :1;
#endif
unsigned qdcount :16;
unsigned ancount :16;
unsigned nscount :16;
unsigned arcount :16;
} HEADER;
static int _srv_compare(const void *a, const void *b) {
dns_host_t ah = * (dns_host_t *) a, bh = * (dns_host_t *) b;
dns_srv_t arr, brr;
if(ah == NULL) return 1;
if(bh == NULL) return -1;
arr = (dns_srv_t) ah->rr;
brr = (dns_srv_t) bh->rr;
if(arr->priority > brr->priority) return 1;
if(arr->priority < brr->priority) return -1;
if(arr->rweight > brr->rweight) return -1;
if(arr->rweight < brr->rweight) return 1;
return 0;
}
#ifdef HAVE_RES_QUERY
#if PACKETSZ > 1024
# define MAX_PACKET PACKETSZ
#else
# define MAX_PACKET (1024)
#endif
typedef union {
HEADER hdr;
unsigned char buf[MAX_PACKET];
} dns_packet_t;
static void *_a_rr(dns_packet_t packet, unsigned char *eom, unsigned char **scan) {
struct in_addr in;
NS_GET32(in.s_addr, *scan);
in.s_addr = ntohl(in.s_addr);
return strdup(inet_ntoa(in));
}
static void *_aaaa_rr(dns_packet_t packet, unsigned char *eom, unsigned char **scan) {
char addr[INET6_ADDRSTRLEN];
struct sockaddr_in6 sa6;
int i;
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
#ifdef SIN6_LEN
sa6.sin6_len = sizeof(sa6);
#endif
for(i = 0; i < 16; i++) {
sa6.sin6_addr.s6_addr[i] = (*scan)[i];
}
j_inet_ntop((struct sockaddr_storage *)&sa6, addr, sizeof(addr));
return strdup(addr);
}
static void *_srv_rr(dns_packet_t packet, unsigned char *eom, unsigned char **scan) {
unsigned int priority, weight, port;
int len;
char host[256];
dns_srv_t srv;
NS_GET16(priority, *scan);
NS_GET16(weight, *scan);
NS_GET16(port, *scan);
len = dn_expand(packet.buf, eom, *scan, host, 256);
if (len < 0)
return NULL;
*scan = (unsigned char *) (*scan + len);
srv = (dns_srv_t) malloc(sizeof(struct dns_srv_st));
srv->priority = priority;
srv->weight = weight;
srv->port = port;
if(weight != 0)
srv->rweight = 1 + rand() % (10000 * weight);
else
srv->rweight = 0;
strcpy(srv->name, host);
return (void *) srv;
}
dns_host_t dns_resolve(const char *zone, int query_type) {
char host[256];
dns_packet_t packet;
int len, qdcount, ancount, an, n;
unsigned char *eom, *scan;
dns_host_t *reply, first;
unsigned int t_type, type, class, ttl;
if(zone == NULL || *zone == '\0')
return NULL;
switch(query_type)
{
case DNS_QUERY_TYPE_A:
t_type = ns_t_a;
break;
case DNS_QUERY_TYPE_AAAA:
t_type = ns_t_a6;
break;
case DNS_QUERY_TYPE_SRV:
t_type = ns_t_srv;
break;
default:
return NULL;
}
if((len = res_query(zone, ns_c_in, t_type, packet.buf, MAX_PACKET)) == -1 || len < sizeof(HEADER))
return NULL;
qdcount = ntohs(packet.hdr.qdcount);
ancount = ntohs(packet.hdr.ancount);
eom = (unsigned char *) (packet.buf + len);
scan = (unsigned char *) (packet.buf + sizeof(HEADER));
while(qdcount > 0 && scan < eom) {
qdcount--;
if((len = dn_expand(packet.buf, eom, scan, host, 256)) < 0)
return NULL;
scan = (unsigned char *) (scan + len + NS_QFIXEDSZ);
}
reply = (dns_host_t *) malloc(sizeof(dns_host_t) * ancount);
memset(reply, 0, sizeof(dns_host_t) * ancount);
an = 0;
while(ancount > 0 && scan < eom ) {
ancount--;
len = dn_expand(packet.buf, eom, scan, host, 256);
if(len < 0) {
for(n = 0; n < an; n++)
free(reply[n]);
free(reply);
return NULL;
}
scan += len;
NS_GET16(type, scan);
NS_GET16(class, scan);
NS_GET32(ttl, scan);
NS_GET16(len, scan);
if(type != t_type) {
scan = (unsigned char *) (scan + len);
continue;
}
reply[an] = (dns_host_t) malloc(sizeof(struct dns_host_st));
reply[an]->type = type;
reply[an]->class = class;
reply[an]->ttl = ttl;
reply[an]->next = NULL;
switch(type)
{
case ns_t_a:
reply[an]->rr = _a_rr(packet, eom, &scan);
break;
case ns_t_a6:
reply[an]->rr = _aaaa_rr(packet, eom, &scan);
break;
case ns_t_srv:
reply[an]->rr = _srv_rr(packet, eom, &scan);
break;
default:
scan = (unsigned char *) (scan + len);
continue;
}
if(reply[an]->rr == NULL)
{
free(reply[an]);
reply[an] = NULL;
break;
}
an++;
}
if(t_type == ns_t_srv)
qsort(reply, an, sizeof(dns_host_t), _srv_compare);
for(n = 0; n < an - 1; n++)
reply[n]->next = reply[n + 1];
first = reply[0];
free(reply);
return first;
}
#endif
#ifdef HAVE_DNSQUERY
#ifndef DNS_TYPE_SRV
# define DNS_TYPE_SRV (33)
#endif
#ifndef DNS_TYPE_AAAA
# define DNS_TYPE_AAAA (28)
#endif
static void *_a_rr(DNS_A_DATA *data) {
struct in_addr in;
in.s_addr = data->IpAddress;
return strdup(inet_ntoa(in));
}
static void *_aaaa_rr(DNS_AAAA_DATA *data) {
char addr[INET6_ADDRSTRLEN];
struct sockaddr_in6 sa6;
int i;
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
#ifdef SIN6_LEN
sa6.sin6_len = sizeof(sa6);
#endif
for(i = 0; i < 4; i++)
sa6.sin6_addr.s6_addr32[i] = data->Ip6Address.IP6Dword[i];
j_inet_ntop((struct sockaddr_storage *) &sa6, addr, sizeof(addr));
return strdup(addr);
}
static void *_srv_rr(DNS_SRV_DATA *data) {
dns_srv_t srv;
srv = (dns_srv_t) malloc(sizeof(struct dns_srv_st));
srv->priority = data->wPriority;
srv->weight = data->wWeight;
srv->port = data->wPort;
if(srv->weight != 0)
srv->rweight = 1 + rand() % (10000 * srv->weight);
else
srv->rweight = 0;
strncpy(srv->name, data->pNameTarget, 255);
srv->name[255] = 0;
return (void *) srv;
}
dns_host_t dns_resolve(const char *zone, int query_type) {
int type, num, i;
PDNS_RECORD rr, scan;
dns_host_t *reply, first;
if(zone == NULL || *zone == '\0')
return NULL;
switch(query_type) {
case DNS_QUERY_TYPE_A:
type = DNS_TYPE_A;
break;
case DNS_QUERY_TYPE_AAAA:
type = DNS_TYPE_AAAA;
break;
case DNS_QUERY_TYPE_SRV:
type = DNS_TYPE_SRV;
break;
default:
return NULL;
}
if(DnsQuery(zone, type, DNS_QUERY_STANDARD, NULL, &rr, NULL) != 0)
return NULL;
num = 0;
for(scan = rr; scan != NULL; scan = scan->pNext)
num++;
reply = (dns_host_t *) malloc(sizeof(dns_host_t) * num);
memset(reply, 0, sizeof(dns_host_t) * num);
num = 0;
for(scan = rr; scan != NULL; scan = scan->pNext) {
if(scan->wType != type || stricmp(scan->pName, zone) != 0)
continue;
reply[num] = (dns_host_t) malloc(sizeof(struct dns_host_st));
reply[num]->type = scan->wType;
reply[num]->class = 0;
reply[num]->ttl = scan->dwTtl;
reply[num]->next = NULL;
switch(type) {
case DNS_TYPE_A:
reply[num]->rr = _a_rr(&scan->Data.A);
break;
case DNS_TYPE_AAAA:
reply[num]->rr = _aaaa_rr(&scan->Data.AAAA);
break;
case DNS_TYPE_SRV:
reply[num]->rr = _srv_rr(&scan->Data.SRV);
break;
}
num++;
}
if(type == DNS_TYPE_SRV)
qsort(reply, num, sizeof(dns_host_t), _srv_compare);
for(i = 0; i < num - 1; i++)
reply[i]->next = reply[i + 1];
first = reply[0];
free(reply);
return first;
}
#endif
void dns_free(dns_host_t dns) {
dns_host_t next;
while(dns != NULL) {
next = dns->next;
free(dns->rr);
free(dns);
dns = next;
}
}