#define WIN32_LEAN_AND_MEAN #include #include #include #include #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++] = '\0'; 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; (iqncount)) && (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= 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, '\0', 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, '\0', 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, '\0', 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, '\0', 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; } }