Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Getting an MX record (the easy way)

0.00/5 (No votes)
27 Feb 2003 1  
Using DNSQuery() to get an MX record

Introductuion

A few months ago I was working on an application that would send emails to users that had subscribed to a newsletter. I know that there are numerous mass mail applications but I really wanted to develop an in-house solution. What I present in this article is a set of classes that are used by the system to validate email addresses.

Credit where it's due

As has come to be my modus operandi in my articles, I have to give thanks to James Oldroyd for helping me find the ::DnsQuery() function. James is an amazing coder and if you can get him to stop thinking in code for more than five minutes you can even have a conversation with him. Before using ::DNSQuery() I had examined some C code that connected to the DNS server and did an MX record request � a real nasty mess of code to say the least. So finding and then using the ::DNSQuery() function was a no-brainer.

Overview

The demo application is a Windows Console Application that accepts email addresses interactively. Entering an email address and pressing the RETURN key starts the email address validation process. Typing exit and pressing the RETURN key will exit the application. The simple demo makes use of two classes that I am describing in this article, CGetSMTPHostName which makes use of ::DnsQuery() and CVerifySMTPEmailAddress which does the actual email validation. It should be noted that I decided to split the classes up into two distinct classes because of the way that I intend to use them but they could be combined into a single class. Below I give a brief description of each.

CGetSMTPHostName

The CGetSMTPHostName class provides three member functions: GetSmtpHostName(), ResetHostMap() and Dump(). It also contains a std::map that holds the DomainName and SMTPHostName pair.

CGetSMTPHostName::GetSmtpHostName()

This method accepts an email address and a CString& to return the host name for the email address. The host name is found using the ::DNSQuery() function. The function starts by stripping the Domain part of the email address and looking in our std::map to determine if we have previously performed a lookup on this domain. If so, we return the host name and we are done. If not, then we make a call to ::DNSQuery() to determine the hostname. If the call is successful we insert the domain and hostname pair into the std::map and return. Here's the code:
BOOL CGetSMTPHostName::GetSmtpHostName(CString _EmailAddress, 
CString& _HostName) { BOOL bRV = TRUE; _HostName.Empty(); int start = _EmailAddress.Find('@')+1; CString strDomain
= _EmailAddress.Mid(start,_EmailAddress.GetLength()-start); if( ! strDomain.IsEmpty()) { // First, look in our map to see if we have looked up this SMTP
// Host before
m_SMTPHostIterator = m_SMTPHost.find(strDomain); if(m_SMTPHostIterator != m_SMTPHost.end()) { // Great, we've looked this one up before... _HostName = (*m_SMTPHostIterator).second; // Is it unknown??? if(_HostName == "UNK") { bRV=FALSE; _HostName.Empty(); } } else { // OK, we haven't looked this up before, so look it up DNS_RECORD* ppQueryResultsSet = NULL; DNS_STATUS statusDNS = ::DnsQuery( strDomain, DNS_TYPE_MX, DNS_QUERY_STANDARD, NULL, &ppQueryResultsSet, NULL ); if(statusDNS == ERROR_SUCCESS) { // Found the SMTP Host Name, insert it into our map _HostName = ppQueryResultsSet->Data.MX.pNameExchange; m_SMTPHost.insert(HostMapValue(strDomain,_HostName)); } else { // I have opted to place unknown domains in the map to reduce // the time that I spend looking up the domain names. You // may want to implement this differently for your usage. bRV = FALSE; DNS_STATUS theError = statusDNS; m_SMTPHost.insert(HostMapValue(strDomain,"UNK")); } } } else { // OOPS, is this a valid email address? bRV = FALSE; } return(bRV); }

CGetSMTPHostName::ResetHostMap()

This method is nothing more than a utility function to clear the std::map in the event that you need to start fresh.

CGetSMTPHostName::Dump()

Another utility function to print the the console the list of domains and hostnames that are contained within the std::map.

CVerifySMTPEmailAddress

This class contains (has a) CGetSMTPHostName class as a private member and had a single public method called VerifyEmailAddress() shown below:
BOOL CVerifySMTPEmailAddress::VerifyEmailAddress(CString _EmailAddress)
{
    CString strHostName;
    if(m_GetSmtpHostName.GetSmtpHostName(_EmailAddress, strHostName))    
    {
    return(Connect(strHostName, _EmailAddress));
    }
    return(FALSE);
}

main() or _tmain()

Here's the main function of the console application:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    // initialize MFC and print and error on failure

    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        // TODO: change error code to suit your needs

        cerr << _T("Fatal Error: MFC initialization failed") << endl;
        nRetCode = 1;
    }
    else
    {
        CVerifySMTPEmailAddress vrfyemail;
    CString strSMTPAddress;
    CString strHostName;
        

    char    caSMTPAddress[128];
    while(TRUE)
    {
            std::cout << "Enter SMTP Address : ";
        std::cin >> caSMTPAddress;
        strSMTPAddress = caSMTPAddress;
        strSMTPAddress.MakeLower();
        if(strSMTPAddress == "exit")
        {
            break;
        }
        if(vrfyemail.VerifyEmailAddress(strSMTPAddress))
        {
            std::cout << strSMTPAddress.GetBuffer(0) << " is VALID" 
<< std::endl; } else { std::cout << strSMTPAddress.GetBuffer(0) << vrfyemail.GetLastError()
<< std::endl; } strSMTPAddress.Empty(); } } return nRetCode; }

Notes

::DNSQuery()[^] can be used for much more than MX Record lookups. Information can be found on MSDN (click the link).

I prefer to use #pragma comment to link in libraries, especially on demonstration applications such as this. You can set up the link libraries to suit your taste.

Conclussion

I wrote this article for one purpose and that was to show that the libraries provided by Microsoft are bountiful. So before you start writing a DNS MX Record lookup function or the like take a look in MSDN or ask a living MSDN index (a.k.a. James) if an existing library contains the functionality that you need.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here