People submit email addresses as well as other types of information in web forms every day (from the
"To:" box of email compose page, to the "Verify email address" field which we complete when registering a pay pal account.)
Usually, the information you submit is checked on the server side and you will be asked to fix possible errors, if any. This procedure is called
"form validation". But you may have noticed the low level of validation of email addresses you submit in the form, which
is usually restricted to only confirming that the email address submitted has "@" and "." signs within.
Note: An article about complete form validation in ASP is available here.
Finger is one of TCP/IP services, used to reconfirm the existence of an
account on a server. You can finger "hojjat@domain.com" to ensure that "domain.com" mail server has a "hojjat"
email account. But for privacy reasons, most mail servers turn this service off. That is why the email address one provides in a web form can not
be "completely" verified on the server side. But this is not the end of the story ...!
Note:Finger is actually used to locate people on other Internet sites
There is another service called MX Lookup. By using this we can ensure that "domain.com" is a mail server. (Actually MX stands for Mail eXchange and MX Lookup means to look for registered
MX records on a DNS.) This way we can at least prevent fake/misspelled email addresses at host level.
What we are going to do in this article is to design a page that can get an email address, validate it by performing a MX lookup, and return an
error message if the email address failed to pass the MX lookup test.
Strategy
Obviously we have to perform some server side checking. Unfortunately ASP does not have a built-in feature to be used for MX lookup. But you can
find this feature on all NT based Windows operating systems (which of course includes the server versions, Windows 2000 and Windows 2003, as well
as your own Windows XP Home Edition, etc.) On the command prompt you can run nslookup.exe to perform Name Server lookups, and by setting some options,
you can restrict this tool to only look for MX records on the name server. So one strategy can be to develop a code which can run nslookup, get
the results, process them, and report the validity of email address.
The above strategy is fast and reliable, but many web hosts prevent users from executing programs from inside ASP, because of security reasons.
If this is the case, we can change our strategy: There are several web sites on the Internet which perform MX lookups for free. All you do is to
enter the email address, and the site will automatically perform the lookup, and report the results to you. Thus the second strategy is to develop
an ASP solution capable to connect to such a web site, send the required information, receive the reports, process it, and finally report the validity
of email address.
Each of the above two strategies are explained below, separately. But before we start I have to state the both services use some Microsoft features
commonly found on Windows based web hosts, thus, unfortunately, these may not work on Unix/Linux based web hosts.
Strategy #1: Running nslookup.exe
Lets get familiar with nslookup.exe first.
First of all we go to Command Prompt by clicking on Start, then Run..., then typing %comspec% and hitting
OK. Then we can test nslookup.exe to get the MX records for a mail server (here yahoo.com) by typing:
nslookup -type=mx yahoo.com
Note: %comspec% is a substitute for cmd.exe (windows 2000 and above) or command.com (windows 98 and ME.)
What we get is like this:
If the ASP user has enough permissions set to execute programs on the server, then it can run nslookup.exe
just as above with such a code:
FUNCTION MXLookup(host)
SET objShell = Server.CreateObject("Wscript.Shell")
DIM objExec, strResult
SET objExec = objShell.Exec("%comspec% /c nslookup -type=MX yahoo.com")
WHILE objExec.Status <> WshFinished
'Wait for the command to be run completely
WEND
strResult = objExec.StdOut.ReadAll
Response.Write replace(strResult,Chr(13),"<br>")
END FUNCTION
Note: On Windows servers, the ASP user is actually the Internet Guest Account. Its name is IUSR_computername and in our case,
it must at least have "Read and Execute" permission on cmd.exe or command.exe file in %windir%\system32 or %windir%
directory, respectively.
The above code uses Windows Script Host component to create a shell object which allows ASP to run commands, and perform many other useful actions.
When nslookup finishes, a string is sent back to the ASP user, which is saved in strResult. This string uses carriage return character ( equal to
Chr(13) in ASP) at the end of each line to go to a new line. We use HTML <br> command for new line, so we have to replace carriage returns
with <br>, the code of which is placed in the last line of the function.
You may have noticed that function gets a host argument but is not using it. This is our next step: To change the function to lookup for the host
given to it, and return "Accepted" if there is a MX records found for the host, or "Denied" if there is not. Here is the code:
FUNCTION MXLookup(host)
SET objShell = Server.CreateObject("Wscript.Shell")
DIM objExec, strResult
SET objExec = objShell.Exec("%comspec% /c nslookup -type=MX " & host)
WHILE objExec.Status <> WshFinished
'Wait for the command to be run completely
WEND
strResult = objExec.StdOut.ReadAll
Reflect(strResult)
END FUNCTION
FUNCTION Reflect(str)
DIM lines, accepted : accepted = -1
lines = Split(str,chr(13))
FOR i = 0 TO UBound(lines)
IF InStr(lines(i),"MX preference = ") > 0 THEN
accepted = i
EXIT FOR
END IF
NEXT
IF accepted > -1 THEN
Response.Write "Accepted: " & lines(accepted)
ELSE
Response.Write "Denied! "
END IF
END FUNCTION
The second function searches the resulted string for "MX preference = " phrase. If nslookup has found at least one MX record for the
given host, it will report it by using the above phrase. (Please look at the example again.) If "MX preference = "
phrase is found, the function returns the line with the phrase, otherwise it reports "Denied!". Finally we complete our MX Lookup page
as such (naming it MXlookup.asp):
<%@LANGUAGE="VBSCRIPT"%>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>MX Lookup</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<form action="MXlookup.asp" method="get">
<p> Enter the host name for MX lookup:
<input name="host" type="text" id="host" size="40" value="<%=Request.QueryString("host")%>"
/>
<input type="submit" value=" Lookup " />
</p>
</form>
<%
IF Request.QueryString("host")<>"" THEN MXLookup(Request.QueryString("host"))
FUNCTION MXLookup(host)
SET objShell = Server.CreateObject("Wscript.Shell")
DIM objExec, strResult
SET objExec = objShell.Exec("%comspec% /c nslookup -type=MX "&host)
WHILE objExec.Status <> WshFinished
'Wait for the command to be run completely
WEND
strResult = objExec.StdOut.ReadAll
Reflect(strResult)
END FUNCTION
FUNCTION Reflect(str)
DIM lines, accepted : accepted = -1
lines = Split(str,chr(13))
FOR i = 0 TO UBound(lines)
IF InStr(lines(i),"MX preference = ") > 0 THEN
accepted = i
EXIT FOR
END IF
NEXT
IF accepted > -1 THEN
Response.Write "Accepted: " & lines(accepted)
ELSE
Response.Write "Denied! "
END IF
END FUNCTION
%>
</body>
</html>
The above code is fast and reliable. How ever, it has restrictions. As we stated above the ASP user must have enough permissions to run %comspec%
on the server. Also, there is a probability that Windows Script Host is not available on the server.
Strategy #2: Using free MX lookup web sites
The second strategy uses another feature in ASP named XMLHTTP. An object made from XMLHTTP class is
used to connect to other servers, send and receive data, which is used to be processed and/or shown to the user. You can send both GET and POST
types of HTTP request, and get the returned code by the server, all in server side. (It is actually like that ASP user is a human, connecting to
that web site with a browser, and copy the results and paste it to YOU as it's user!)
Note: If you need more information about the XMLHTTP component, here's a good resource for you.
No parts of our previous code are going to change except the MXLookup function. But before we write the whole code for this function, let's start
with a test function that introduces XMLHTTP to you:
FUNCTION MXLookup(host)
MXLookUp = False
Dim objXMLHTTP,strResult
Set objXMLHTTP = Server.CreateObject("Microsoft.XMLHTTP")
objXMLHTTP.Open "Get","http://www.asp101.com", False
objXMLHTTP.Send
strResult = objXMLHTTP.ResponseText
Response.Write strResult
END FUNCTION
The above code connects to the asp101 web site, retrieves data from it, and then Response.Writes the result to you, so you can see the asp101 page
in your self-created ASP page!
Just as simple as you placed "http://www.asp101.com" in the Open method of objXMLHTTP object, you can put a URL with a query string,
like "http://www.domain.com?lang=fr" and get the results (the French version of an assumptive domain.com web site) and return it to the
user. You can also process the data received from the remote site on you server side, and report the results to your user. And this is the key point
in our MXLookup function ....
In the following example, MXLookup function connects to a page in examples.softwaremodeles.com web site, which performs MX lookups. The IntraDns.asp
file on that site needs a query string like domainname=hostname and returns several DNS records for that site. There is an option
named t_mx which, if checked, can make the IntraDns.asp understand that it must only return MX records. We use this to get faster responses
from that server. So what we are going to send to the remote server is like: http://examples.softwaremodules.com/IntraDns.asp?domainname=host&Submit=Submit&t_mx=1
where host is substituted by the host name sent to our MXLookup function. Here is the code:
FUNCTION MXLookup(host)
MXLookUp = False
Dim objXMLHTTP,strResult
Set objXMLHTTP = Server.CreateObject("Microsoft.XMLHTTP")
objXMLHTTP.Open "Get", _
"http://examples.softwaremodules.com/IntraDns.asp?domainname=" & host & "&Submit=Submit&t_mx=1",
False
objXMLHTTP.Send
strResult = objXMLHTTP.ResponseText
strResult = Mid(strResult,InStr(strResult,"(MX) for <strong>"),100)
strResult = Mid(strResult,Instr(strResult,"</strong>. Items Returned: <strong>")+35,1)
IF CInt(strResult) > 0 THEN
MXLookUp = TRUE
ELSE
MXLookUp = FALSE
END IF
END FUNCTION
As you may have noticed, the above code processes the returned string too. Actually, the IntraDns.asp file on the remote server returns the MX
records, if any in such format:
DNS Lookup (MX) for yahoo.com. Items Returned: 4
mx4.mail.yahoo.com - MX Priority: 5
mx1.mail.yahoo.com - MX Priority: 1
mx2.mail.yahoo.com - MX Priority: 1
mx3.mail.yahoo.com - MX Priority: 1
Therefore, our MXLookup function looks for phrase "(MX) for <strong>" in the returned HTML, and if it is found, it looks for the
number of MX records found by IntraDns.asp somewhere more than 35 characters after the "MX for <strong>" phrase. To be more precise,
it only read the FIRST digit from the LEFT side, of that number. This digit is "0" only if the number of MX records was zero. If three
records are found, the first digit from the left side is "3". If twenty MX records are found, the first left digit is "2".
I used the double-quotes wisely in the last three sentences! Yes, the first left digit is a String now! We have to change it to an integer
(using CInt() method), and then compare it with 0. If it is greater than 0, our function returns TRUE, else it returns FALSE.
We can use this function in our code to report "Accepted." or "Denied!", just like our first strategy. Here is the code:
<%@LANGUAGE="VBSCRIPT"%>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>MX Lookup</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<form action="MXlookup.asp" method="get">
<p> Enter the host name for MX lookup:
<input name="host" type="text" id="host" size="40" value="<%=Request.QueryString("host")%>"
/>
<input type="submit" value=" Lookup " />
</p>
</form>
<%
IF Request.QueryString("host")<>"" THEN
result = MXLookup(Request.QueryString("host"))
IF result = TRUE THEN
Response.Write "Accepted."
ELSE
Response.Write "Denied!"
END IF
END IF
FUNCTION MXLookup(host)
MXLookUp = False
Dim objXMLHTTP,strResult
Set objXMLHTTP = Server.CreateObject("Microsoft.XMLHTTP")
objXMLHTTP.Open "Get", _
"http://examples.softwaremodules.com/IntraDns.asp?domainname=" & host & "&Submit=Submit&t_mx=1",
False
objXMLHTTP.Send
strResult = objXMLHTTP.ResponseText
strResult = Mid(strResult,InStr(strResult,"(MX) for <strong>"),100)
strResult = Mid(strResult,Instr(strResult,"</strong>. Items Returned: <strong>")+35,1)
IF CInt(strResult) > 0 THEN
MXLookUp = TRUE
ELSE
MXLookUp = FALSE
END IF
END FUNCTION
%>
</body>
</html>
You can change the above code a bit, to let it show the first MX record found for accepted hosts, just as the MXLookup function in our first strategy
did.
This second strategy is not as reliable as the first one. Simply, if the remote web site changes the format of the result returned by IntraDns.asp
file, it can hardly affect your ASP code. As it usually takes some time for you to find out such a problem, it means your users will face several
problems during that period of time. A possible solution to this is to reach to a consensus with the owner of the remote site, to be informed before
any changes happen to their site, or something. This article is not going to teach you how to do this, of course! ;o)
Another situation in which the second strategy will not work is when XMLHTTP component is not available on the server hosting your ASP codes.
The second disadvantage of this strategy is it is slow, and uses more resources on the server. So try to manage to use the first strategy if you
are planning to validate many email addresses every day.
Author Notes
A friend ran my code on his Windows 2000 web server, but the code didn't
work for him. He also had problems with opening recordset objects, when they
were going to be filled by hand (not from a database.) We made our efforts
to solve his issue, and finally found out that some helpful information in
this microsoft KB article: