Get a mail server's IP address with PHPGet a mail server's IP address with PHP

Posted November 9th, 2012 in PHP

Some time back ago I posted how to do email validation in PHP with the filter_var function (here and here) and have recently been asked how you would determine if the domain is valid. There are a few ways of doing this and I suggested doing DNS lookups. In this post, I show how to work out a mail server's IP address using DNS lookups with PHP.

Do an MX lookup and fall back to A record

One commenter wrote some code which added an MX lookup to the validation routine. The catch with this is there is no requirement for a domain to have an MX record but still serve mail; if there are no matching MX records a mail server is then supposed to attempt to send mail to the IP address specified by the A record(s).

The function below does exactly that: it attempts to find the MX record with the highest priority and will return that. If no MX records can be found, then it returns an A record. If nothing could be found, then it returns false.

The code

The code below can be downloaded here and you can view it below. It's fairly well commented so I won't write any further content about it here, other than to note that just because you don't find a record still doesn't necessarily mean no records exist: the DNS servers may be down or otherwise unreachable, or there may be some other issue.

// valid tests
echo 'google.com    : ' . mailIPAddress('google.com') . "\n";
echo 'microsoft.com : ' . mailIPAddress('microsoft.com') . "\n";
echo 'facebook.com  : ' . mailIPAddress('facebook.com') . "\n";
// invalid test
echo 'fyjit4w9.com  : ' . mailIPAddress('fyjit4w9.com') . "\n";

/**
 * Returns an IP address that mail can be delivered to for the passed in domain.
 *
 * First a check is done to find the MX record with the highest priority. If no
 * MX records are found, will then do an A record lookup. If no records are 
 * found, false is returned.
 *
 * Note that an IP address being returned is no guarantee that there is a mail
 * server listening at that IP address, or that it will accept mail for the 
 * domain being queried. It simply returns values based on the DNS records.
 *
 * @param $domain string The domain to query
 * @return bool|string Returns false if an MX/A record could not be found. 
 *   Returns the dotted IPv4 address if one was found.
 */
function MailIPAddress($domain) {

	$ip = false;

	// first try to get MX records

	// the lower the 'pri' value (priority) of MX hosts, the higher its 
	// precedence. if there are 3 MX records for a domain with priority 
	// 10, 20 and 30, a mail server should attempt delivery to that with
	// priority 10 first. if that fails, then 20, and so on. the numeric
	// value in the MX record is abitrary and there's no standard for what
	// it should be set to. the values could just as easily be 1, 2, 3 
	// but are typically 10, 20, 30.

	// the order of records in the array returned by dns_get_record is not
	// necessarily in order of priority, so we have to loop through the
	// array and work out which has the highest priority. this is done
	// with the $priority variable and doing a comparison on each loop
	// to see if this record has a higher priority than the previous ones

	$records = dns_get_record($domain, DNS_MX);
	$priority = null;
	foreach($records as $record) {
		if($priority == null || $record['pri'] < $priority) {
			$myip = gethostbyname($record['target']);
			// if the value returned is the same, then the lookup failed
			if($myip != $record['target']) {
				$ip = $myip;
				$priority = $record['pri'];
			}
		}
	}

	// if no MX record try A record

	// if no MX records exist for a domain, mail servers are supposed to 
	// attempt delivery instead to the A record for the domain. the final
	// check done here is to see if an A record exists, and if so, that
	// will be returned

	if(!$ip) {
		$ip = gethostbyname($domain);
		// if the value returned is the same, then the lookup failed
		if($ip == $domain) {
			$ip = false;
		}
	}

	return $ip;

}

Back to email validation

While I would never do this myself, for the sake of the people commenting on the other post, here's how you can combine this function with the email validation function and attempting to ensure the domain name is valid. Again note that just because false is returned doesn't necessarily mean there are no records - something else may have gone wrong.

$email = 'chris@example.com';
list($emailPart, $domainPart) = explode('@', $email);
if(filter_var($email, FILTER_VALIDATE_EMAIL) && MailIPAddress($domainPart)) {
    // it's valid so do something
}
else {
    // it's not valid so do something else
}

Related posts:

Comments

blog comments powered by Disqus