Click here to Skip to main content
15,884,176 members
Articles / Desktop Programming / MFC
Article

Getting an MX record (the easy way)

Rate me:
Please Sign up or sign in to vote.
4.55/5 (16 votes)
27 Feb 20033 min read 138.9K   2.1K   35   24
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, <BR>                                       CString& _HostName)
{
    BOOL bRV = TRUE;
    _HostName.Empty();
    int start = _EmailAddress.Find('@')+1;
    CString strDomain<BR>                   = _EmailAddress.Mid(start,_EmailAddress.GetLength()-start);
    if( ! strDomain.IsEmpty())
    {
         // First, look in our map to see if we have looked up this SMTP <BR>         // 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" <BR>                      << std::endl;
        }
        else
        {
        std::cout << strSMTPAddress.GetBuffer(0) << vrfyemail.GetLastError() <BR>                  << 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


Written By
Web Developer
United States United States
Dave has been programming for the past 20+ years first on a variety of platforms and operating systems using various languages. As a hobbyist Dave cut his teeth on the Commodore Pet and the 64 coding in basic and then moving to 6502 ASM. Dave moved to the Amiga using 68000 ASM and then C. His knowledge of the C language offered the stepping stone for him to make his hobby his profession taking a position coding C on an AIX Unix platform. Since then he has worked on many flavors of Unix, QNX, Windows (3.11 – present), and has been coding games for his Pocket PC in his spare time.

Dave lives in Indiana with his two teenage daughters and two cats.

Comments and Discussions

 
Questionm_SMTPServer.Connect(_HostName... Is failing every time Pin
Member 471758915-Jan-17 20:29
Member 471758915-Jan-17 20:29 
Generalnice job Pin
skumria5-Sep-06 8:58
skumria5-Sep-06 8:58 
QuestionDnsQuery API timeout? Pin
elbazon14-Aug-05 23:46
elbazon14-Aug-05 23:46 
GeneralDnsQuery ErrorCode 9002 Pin
_happymozart_11-May-05 9:06
_happymozart_11-May-05 9:06 
GeneralDnsQuery() Is a very dependent function Pin
G_S14-Feb-05 13:37
G_S14-Feb-05 13:37 
GeneralDNS adding/modifying zone records Pin
kumarbp10-Oct-04 17:24
kumarbp10-Oct-04 17:24 
GeneralDNS adding/modifying zone records Pin
kumarbp10-Oct-04 17:24
kumarbp10-Oct-04 17:24 
Generalthanks, before i use udp and tcp to query it Pin
SevenCat24-Oct-03 23:17
SevenCat24-Oct-03 23:17 
Generalwrite some code, compare the Preference(rfc requir this)) Pin
SevenCat27-Oct-03 18:33
SevenCat27-Oct-03 18:33 
GeneralDNSQuery not available on Win9x Pin
Marc G4-Sep-03 4:05
Marc G4-Sep-03 4:05 
Questionhow to get more than one mx records? Pin
bborn12-Aug-03 20:18
bborn12-Aug-03 20:18 
AnswerRe: how to get more than one mx records? Pin
Paul Ingles25-Oct-03 0:36
Paul Ingles25-Oct-03 0:36 
GeneralJust being picky ;-) Pin
pierrec16-Jul-03 5:04
pierrec16-Jul-03 5:04 
QuestionCan i use to query spamcop.net ? Pin
ScorpioMidget21-May-03 5:52
ScorpioMidget21-May-03 5:52 
GeneralLinker error Pin
Anthony_Yio2-Mar-03 16:58
Anthony_Yio2-Mar-03 16:58 
GeneralRe: Linker error Pin
Anthony_Yio2-Mar-03 17:04
Anthony_Yio2-Mar-03 17:04 
Generalspam Pin
zoldat2-Mar-03 15:40
zoldat2-Mar-03 15:40 
GeneralRe: spam Pin
jeoshah19-Jun-03 1:13
jeoshah19-Jun-03 1:13 
GeneralGood attempt, but.... Pin
hector santos1-Mar-03 2:16
hector santos1-Mar-03 2:16 
GeneralRe: Good attempt, but.... Display Correction Pin
hector santos1-Mar-03 2:20
hector santos1-Mar-03 2:20 
GeneralRe: Good attempt, but.... Display Correction Pin
Gisle Vanem1-Mar-03 2:45
Gisle Vanem1-Mar-03 2:45 
GeneralRe: Good attempt, but.... Display Correction Pin
hector santos1-Mar-03 3:12
hector santos1-Mar-03 3:12 
GeneralRe: Good attempt, but.... Display Correction Pin
shankargiri8-Feb-11 7:55
shankargiri8-Feb-11 7:55 
GeneralRe: Good attempt, but.... Pin
Dave Loeser1-Mar-03 4:38
Dave Loeser1-Mar-03 4:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.