Using Mx Records to Validate Email Addresses

Using MX records to validate email addresses

I see no harm doing a MX lookup with checkdnsrr() and I also don't see how false positives may appear. You don't need to escape the hostname, in fact you can use this technique and take it a little further by talking to the MTA and testing if the user exists at a given host (however this technique may and probably will get you some false positives in some hosts).

Should e-mail validation using DNS/MX record be used?

Most sites, especially ones that retain login information, send confirmation emails after you sign up. This not only confirms the email address is valid but also confirms that the email address belongs to the submitter. Having a DNS lookup would be redundant unless you wanted to tip off the user that they misspelled their email address.

And if I wanted to put in a fake email address, aaa@aaa.com (AAA, the auto club) and somebody@example.com (from RFC 2606) would both pass. I don't think a DNS lookup would catch as many fake addresses as you think.

How do I validate the MX record for a domain in python?

There is no DNS interface in the standard library so you will either have to roll your own or use a third party library.

This is not a fast-changing concept though, so the external libraries are stable and well tested.

The one I've used successful for the same task as your question is PyDNS.

A very rough sketch of my code is something like this:

import DNS, smtplib

DNS.DiscoverNameServers()
mx_hosts = DNS.mxlookup(hostname)

# Just doing the mxlookup might be enough for you,
# but do something like this to test for SMTP server
for mx in mx_hosts:
smtp = smtplib.SMTP()
#.. if this doesn't raise an exception it is a valid MX host...
try:
smtp.connect(mx[1])
except smtplib.SMTPConnectError:
continue # try the next MX server in list

Another library that might be better/faster than PyDNS is dnsmodule although it looks like it hasn't had any activity since 2002, compared to PyDNS last update in August 2008.

Edit: I would also like to point out that email addresses can't be easily parsed with a regexp. You are better off using the parseaddr() function in the standard library email.utils module (see my answer to this question for example).

Validate e-mail address with mx record validation in javascript/Angular/Ionic

You have to use a webservice for that. Create a small service that validates through mx validation and return the result to js

Zend Validate Email Address and Deep MX Checking

As the comments on my post allude, there is a bug in Zend_Validate_EmailAddress::_isReserved. Not only is it buggy, but it's difficult to understand the logic flow. It was a private function, so I changed it to protected so I could override it in my sub-class. There were also some incorrect ranges in the $_invalidIp array.

For my logic check, I decided that the simplest (clearest?) way to compare IP addresses was to convert them to their decimal integer equivalents.

Here's my sub-class:

class My_Validate_EmailAddressDeep extends Zend_Validate_EmailAddress
{
/**
* @var array
*/
protected $_messageTemplates = array(
self::INVALID => "Invalid type given. String expected",
self::INVALID_FORMAT => "'%value%' is not a valid email address in the basic [user]@[hostname] format",
self::INVALID_HOSTNAME => "The '%hostname%' part of '%value%' is not a valid hostname",
self::INVALID_MX_RECORD => "'%hostname%' does not appear to be configured to accept email",
self::INVALID_SEGMENT => "'%hostname%' does not appear to be configured to accept external email",
self::DOT_ATOM => null,
self::QUOTED_STRING => null,
self::INVALID_LOCAL_PART => "The '%localPart%' part of '%value%' is not valid",
self::LENGTH_EXCEEDED => "'%value%' is longer than the allowed length for an email address",
);

/**
* Internal options array
* @var array
*/
protected $_options = array(
'allow' => Zend_Validate_Hostname::ALLOW_DNS,
'deep' => true,
'domain' => true,
'hostname' => null,
'mx' => true,
);

/**
* @see http://en.wikipedia.org/wiki/Reserved_IP_addresses#Reserved_IPv4_addresses
* @var array [first octet] => [[CIDR] => [[range start], [range end]]]
*/
protected $_reservedIps = array(
'0' => array('0.0.0.0/8' => array('0.0.0.0', '0.255.255.255',),),
'10' => array('10.0.0.0/8' => array('10.0.0.0', '10.255.255.255',),),
'127' => array('127.0.0.0/8' => array('127.0.0.0', '127.255.255.255',),),
'169' => array('169.254.0.0/16' => array('169.254.0.0', '169.254.255.255',),),
'172' => array('172.16.0.0/12' => array('172.16.0.0', '172.31.255.255',),),
'192' => array(
'192.0.2.0/24' => array('192.0.2.0', '192.0.2.255',),
'192.88.99.0/24' => array('192.88.99.0', '192.88.99.255',),
'192.168.0.0/16' => array('192.168.0.0', '192.168.255.255',),
),
'198' => array(
'198.18.0.0/15' => array('198.18.0.0', '198.19.255.255',),
'198.51.100.0/24' => array('198.51.100.0', '198.51.100.255',),
),
'203' => array('203.0.113.0/24' => array('203.0.113.0', '203.0.113.255',),),
'224' => array('224.0.0.0/4' => array('224.0.0.0', '239.255.255.255',),),
'240' => array('240.0.0.0/4' => array('240.0.0.0', '255.255.255.255',),),
);

/**
* Returns if the given host is reserved
*
* @param string $host
* @return boolean
*/
protected function _isReserved($host)
{
if (!preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $host)) {
$host = gethostbyname($host);
}

$octets = explode('.', $host);
if (224 <= (int) $octets[0]) {
// IP Addresses beginning with 224 or greater are all reserved, short-circuit range checks
return true;
} elseif (array_key_exists($octets[0], $this->_reservedIps)) {
// for integer comparisons
$intIp = $this->_ipToInt($host);

// loop over reserved IP addresses
foreach ($this->_reservedIps as $ranges) {
foreach ($ranges as $range) {
if (($this->_ipToInt($range[0]) <= $intIp)
&& ($this->_ipToInt($range[1]) >= $intIp)) {
// the IP address falls in a reserved range
return true;
}
}
}

// the IP address did not fall in a reserved range
return false;
} else {
return false;
}
}

/**
* Convert a dot-decimal IP address to it's decimal integer equivalent
*
* @param string $ip
* @return integer
*/
protected function _ipToInt($ip)
{
$octets = explode('.', $ip);
foreach ($octets as $key => $octet) {
$octets[$key] = str_pad(decbin($octet), 8, '0', STR_PAD_LEFT);
}
$bin = implode('', $octets);
return bindec($bin);
}
}


Related Topics



Leave a reply



Submit