#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <windns.h>
#include <iphlpapi.h>
#include "xdns.h"
#pragma comment(lib, "ws2_32.lib")
#define mx_alloc(n) ((void*)HeapAlloc(GetProcessHeap(),0,(n)))
#define mx_free(p) {HeapFree(GetProcessHeap(),0,(p));}
#define TYPE_MX 15
#define CLASS_IN 1
#pragma pack(push, 1)
struct dnsreq_t {
WORD id;
WORD flags;
WORD qncount;
WORD ancount;
WORD nscount;
WORD arcount;
};
#pragma pack(pop)
struct mx_rrlist_t {
struct mx_rrlist_t *next;
char domain[260];
WORD rr_type;
WORD rr_class;
WORD rdlen;
int rdata_offs;
};
static int mx_dns2qname(const char *domain, unsigned char *buf)
{
int i, p, t;
for (i=0,p=0;;) {
if (domain[i] == 0) break;
for (t=i; domain[t] && (domain[t] != '.'); t++);
buf[p++] = (t - i);
while (i < t) buf[p++] = domain[i++];
if (domain[i] == '.') i++;
}
buf[p++] = ' ';
return p;
}
static int mx_make_query(int sock, struct sockaddr_in *dns_addr, const char *domain, WORD req_flags)
{
unsigned char buf[1024];
int i, tmp;
memset(buf, 0, sizeof(buf));
i = 0;
*(WORD *)(buf+i) = (WORD)(GetTickCount() & 0xFFFF); i += 2;
*(WORD *)(buf+i) = req_flags; i += 2; /* flags */
*(WORD *)(buf+i) = htons(0x0001); i += 2; /* qncount */
*(WORD *)(buf+i) = 0; i += 2;
*(WORD *)(buf+i) = 0; i += 2;
*(WORD *)(buf+i) = 0; i += 2;
tmp = mx_dns2qname(domain, buf+i); i += tmp;
*(WORD *)(buf+i) = htons(TYPE_MX); i += 2;
*(WORD *)(buf+i) = htons(CLASS_IN); i += 2;
tmp = sendto(sock, buf, i, 0, (struct sockaddr *)dns_addr, sizeof(struct sockaddr_in));
return (tmp <= 0) ? 1 : 0;
}
static int mx_skipqn(unsigned char *buf, int pos, int len, struct dnsreq_t *reply_hdr)
{
int i, n;
for (i=0; (i<ntohs(reply_hdr->qncount)) && (pos < len);) {
n = buf[pos];
if (n == 0) {
pos += 5;
i++;
} else if (n < 64) {
pos += 1+n;
} else {
pos += 6;
i++;
}
}
return pos;
}
static int mx_decode_domain(unsigned char *buf, int pos, int len, char *out)
{
int retpos=0, sw, n, j, out_pos;
*out = 0;
for (sw=0, out_pos=0; pos < len;) {
if (out_pos >= 255)
break;
n = (unsigned char)buf[pos];
if (n == 0) {
pos++;
break;
} else if (n < 64) {
pos++;
for (j=0; j<n; j++)
out[out_pos++] = buf[pos++];
out[out_pos++] = '.';
} else {
if (sw == 0) retpos=pos+2;
sw = 1;
n = ntohs(*(WORD *)(buf+pos)) & 0x3FFF;
pos = n;
if (pos >= len) break;
}
}
while (out_pos > 0)
if (out[out_pos-1] != '.') break; else out_pos--;
out[out_pos] = 0;
return (sw == 0) ? pos : retpos;
}
static void mx_free_rrlist(struct mx_rrlist_t *p)
{
struct mx_rrlist_t *q;
while (p != NULL) {
q = p->next;
mx_free(p);
p = q;
}
}
static struct mx_rrlist_t *mx_parse_rr(unsigned char *buf, int reply_len)
{
struct mx_rrlist_t *root, *top, *newrr, tmp_rr;
struct dnsreq_t *reply_hdr;
int i, j, rr, rr_count;
root = top = NULL;
reply_hdr = (struct dnsreq_t *)buf;
if (reply_len < 12) return NULL;
i = 12;
i = mx_skipqn(buf, i, reply_len, reply_hdr);
if (i >= reply_len)
return NULL;
rr_count = reply_hdr->ancount + reply_hdr->nscount + reply_hdr->arcount;
for (rr=0,newrr=NULL; (rr < rr_count) && (i < reply_len); rr++) {
memset(&tmp_rr, ' ', sizeof(struct mx_rrlist_t));
i = mx_decode_domain(buf, i, reply_len, tmp_rr.domain);
if ((i+10) >= reply_len) break;
tmp_rr.rr_type = ntohs(*(WORD*)(buf+i)); i += 2;
tmp_rr.rr_class = ntohs(*(WORD*)(buf+i)); i += 2;
i += 4; /* 32-bit TTL */
tmp_rr.rdlen = ntohs(*(WORD*)(buf+i)); i += 2;
tmp_rr.rdata_offs = i;
if ((tmp_rr.rdlen < 0) || ((i+tmp_rr.rdlen) > reply_len)) break;
j = sizeof(struct mx_rrlist_t) + 16;
newrr = (struct mx_rrlist_t *)mx_alloc(j);
if (newrr == NULL) break;
memset((char *)newrr, ' ', j);
*newrr = tmp_rr;
i += tmp_rr.rdlen;
newrr->next = NULL;
if (top == NULL) {
root = top = newrr;
} else {
top->next = newrr;
top = newrr;
}
}
return root;
}
static struct mxlist_t *my_get_mx_list2(struct sockaddr_in *dns_addr, const char *domain, int *err_stat)
{
int sock, reply_len, rrcode, buf_size;
int loc_retry;
struct timeval tv;
struct fd_set fds;
unsigned char *buf;
unsigned short query_fl;
struct dnsreq_t *reply_hdr;
struct mx_rrlist_t *rrlist=NULL, *rr1;
struct mxlist_t *mxlist_root, *mxlist_top, *mxlist_new;
*err_stat = 1;
buf_size = 4096;
buf = (char *)mx_alloc(buf_size);
if (buf == NULL) return NULL;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == 0 || sock == INVALID_SOCKET) {
mx_free(buf);
return NULL;
}
for (loc_retry=0; loc_retry<2; loc_retry++) {
mxlist_root = mxlist_top = NULL;
if (loc_retry == 0)
query_fl = htons(0x0100);
else
query_fl = htons(0);
if (mx_make_query(sock, dns_addr, domain, query_fl))
continue;
FD_ZERO(&fds); FD_SET(sock, &fds);
tv.tv_sec = 12; tv.tv_usec = 0;
if (select(0, &fds, NULL, NULL, &tv) <= 0)
continue;
memset(buf, ' ', sizeof(buf));
reply_len = recv(sock, buf, buf_size,0);
if (reply_len <= 0 || reply_len <= sizeof(struct dnsreq_t))
continue;
reply_hdr = (struct dnsreq_t *)buf;
rrcode = ntohs(reply_hdr->flags) & 0x0F;
if (rrcode == 3) {
*err_stat = 2;
break;
}
if ((rrcode == 2) && (ntohs(reply_hdr->flags) & 0x80)) {
*err_stat = 2;
break;
}
if (rrcode != 0)
continue;
rrlist = mx_parse_rr(buf, reply_len);
if (rrlist == NULL)
continue;
mxlist_root = mxlist_top = NULL;
for (rr1=rrlist; rr1; rr1=rr1->next) {
if ((rr1->rr_class != CLASS_IN) || (rr1->rr_type != TYPE_MX) || (rr1->rdlen < 3))
continue;
mxlist_new = (struct mxlist_t *)mx_alloc(sizeof(struct mxlist_t));
if (mxlist_new == NULL) break;
memset(mxlist_new, 0, sizeof(struct mxlist_t));
mxlist_new->pref = ntohs(*(WORD *)(buf+rr1->rdata_offs+0));
mx_decode_domain(buf, rr1->rdata_offs+2, reply_len, mxlist_new->mx);
if (mxlist_new->mx[0] == 0) {
mx_free(mxlist_new);
continue;
}
if (mxlist_top == NULL) {
mxlist_root = mxlist_top = mxlist_new;
} else {
mxlist_top->next = mxlist_new;
mxlist_top = mxlist_new;
}
}
if (mxlist_root == NULL) {
mx_free_rrlist(rrlist);
continue;
}
mx_free_rrlist(rrlist);
break;
}
mx_free(buf);
closesocket(sock);
return mxlist_root;
}
struct mxlist_t *my_get_mx_list(struct sockaddr_in *dns_addr, const char *domain)
{
struct mxlist_t *list;
int i, e;
for (i=0; i<2; i++) {
list = my_get_mx_list2(dns_addr, domain, &e);
if (list != NULL) return list;
if (e == 2) /* permanent error */
break;
Sleep(100);
}
return NULL;
}
//-----------------------------------------------------------------------------
typedef DNS_STATUS (WINAPI *DNSQUERYA)(IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP4_ARRAY aipServers OPTIONAL, IN OUT PDNS_RECORD *ppQueryResults OPTIONAL, IN OUT PVOID *pReserved OPTIONAL);
static struct mxlist_t *getmx_dnsapi(const char *domain)
{
HINSTANCE hDnsapi;
DNSQUERYA pDnsQuery_A;
DNS_RECORD *pQueryResults, *pQueryRec;
DNS_STATUS statusDns;
char szDnsApi[] = "dnsapi.dll";
struct mxlist_t *mx_root, *mx_top, *mx_new;
hDnsapi = GetModuleHandle(szDnsApi);
if (hDnsapi == NULL) {
hDnsapi = LoadLibrary(szDnsApi);
if (hDnsapi == NULL) return NULL;
}
pDnsQuery_A = (DNSQUERYA)GetProcAddress(hDnsapi, "DnsQuery_A");
if (pDnsQuery_A == NULL) return NULL;
statusDns = pDnsQuery_A(domain, DNS_TYPE_MX, DNS_QUERY_STANDARD, NULL, &pQueryResults, NULL);
if (statusDns != ERROR_SUCCESS) return NULL;
mx_root = mx_top = NULL;
for (pQueryRec=pQueryResults; pQueryRec; pQueryRec = pQueryRec->pNext) {
if (pQueryRec->wType != DNS_TYPE_MX) continue;
mx_new = (struct mxlist_t *)mx_alloc(sizeof(struct mxlist_t));
if (mx_new == NULL) break;
memset(mx_new, ' ', sizeof(struct mxlist_t));
mx_new->pref = pQueryRec->Data.MX.wPreference;
lstrcpyn(mx_new->mx, pQueryRec->Data.MX.pNameExchange, 255);
if (mx_top == NULL) {
mx_root = mx_top = mx_new;
} else {
mx_top->next = mx_new;
mx_top = mx_new;
}
}
return mx_root;
}
//-----------------------------------------------------------------------------
typedef DWORD (WINAPI *GetNetworkParams_t)(PFIXED_INFO, PULONG);
static struct mxlist_t *getmx_mydns(const char *domain)
{
static const char szIphlpapiDll[] = "iphlpapi.dll";
HINSTANCE hIphlpapi;
GetNetworkParams_t pGetNetworkParams;
char *info_buf;
FIXED_INFO *info;
IP_ADDR_STRING *pa;
DWORD dw, info_buf_size;
struct sockaddr_in addr;
struct mxlist_t *mxlist;
hIphlpapi = GetModuleHandle(szIphlpapiDll);
if (hIphlpapi == NULL || hIphlpapi == INVALID_HANDLE_VALUE)
hIphlpapi = LoadLibrary(szIphlpapiDll);
if (hIphlpapi == NULL || hIphlpapi == INVALID_HANDLE_VALUE) return NULL;
pGetNetworkParams = (GetNetworkParams_t)GetProcAddress(hIphlpapi, "GetNetworkParams");
if (pGetNetworkParams == NULL) return NULL;
info_buf_size = 16384;
info_buf = (char *)mx_alloc(info_buf_size);
dw = info_buf_size;
info = (FIXED_INFO *)info_buf;
if (pGetNetworkParams(info, &dw) != ERROR_SUCCESS)
return NULL;
for (mxlist=NULL,pa=&info->DnsServerList; pa; pa=pa->Next) {
if (pa->IpAddress.String == NULL) continue;
addr.sin_family = AF_INET;
addr.sin_port = htons(53);
addr.sin_addr.s_addr = inet_addr(pa->IpAddress.String);
if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == 0xFFFFFFFF) {
struct hostent *h = gethostbyname(pa->IpAddress.String);
if (h == NULL) continue;
addr.sin_addr = *(struct in_addr *)h->h_addr_list[0];
}
if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == 0xFFFFFFFF)
continue;
mxlist = my_get_mx_list(&addr, domain);
if (mxlist != NULL) break;
}
mx_free(info_buf);
return mxlist;
}
//-----------------------------------------------------------------------------
struct mxlist_t *get_mx_list(const char *domain)
{
struct mxlist_t *p;
if ((p = getmx_dnsapi(domain)) != NULL)
return p;
else
return getmx_mydns(domain);
}
void free_mx_list(struct mxlist_t *p)
{
struct mxlist_t *q;
while (p != NULL) {
q = p->next;
mx_free(p);
p = q;
}
}