Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What would cause checkdnsrr() or dns_get_record() to take too long?

Tags:

php

dns

$domain = 'abasdfasdfac.comlkjljkl';  // Yes, an ugly invalid domain

$start_time = microtime(true);
echo "<p>MX "; 
var_dump(checkdnsrr($domain, 'MX'));
echo "</p>";
$end_time = microtime(true);
echo "<p>run time: " . ($end_time - $start_time) . "</p>";

I am getting times in the order of 60 milliseconds when running this on my development system (Win+XAMPP on residential DSL w/AT&T).

However, when uploaded to the live server and run from there the run time goes up into the 20 second range.

Results are the same if I use @dns_get_record($domain, DNS_MX) instead.

What could be causing this? Is is a matter of AT&T's DNS servers returning with a result faster than whatever my production server is pointing to? Still, twenty seconds seems excessive.

More importantly, how does one fix it?

I am using this as the last stage of email validation. However, I can't have the user waiting for twenty seconds while the DNS lookup returns.

EDIT:

I've looked into this a little further. Using dig from the console is fast on the same server that is taking 20 to 30 seconds to do a DNS check on invalid domains. That might be an important point. Valid domains return quickly with either checkdnsrr() or @dns_get_record.

As an interim measure I am looking into substituting @dns_get_record in my email validity check with a dig-based function I wrote:

// Use "dig" command to get DNS record data
// $type    ANY = Complete record
//          A   = Address Record
//          MX  = Mail Exchange Record
//          CNAME = Canonical Name Record  (http://en.wikipedia.org/wiki/Canonical_name_record)
//
//          more types: http://en.wikipedia.org/wiki/List_of_DNS_record_types
//
// $host    Domain to investigate
//
function dig_get_dns_record($type, $host) 
{ 
    $cleaned_host = escapeshellcmd($host);
    ob_start(); 
        // Note: for this to work on Windows/XAMPP "dig" has to be installed and the search path
        passthru("dig $type $cleaned_host"); 
        $lookup = ob_get_contents(); 
    ob_end_clean(); 
    //echo "<pre>" . $lookup . "</pre>";  // Remove comment to see dig output
    return $lookup; 
}   


// For the purposes of deciding if a domain is real, this checks, the MX, A and CNAME
// and returns FALSE if none are found.  If only one of the three exists we give it
// the benefit of the doubt.
//
// $host    Domain to investigate
//
function has_valid_dns($host)
{
    $result  = dig_get_dns_record("MX", $host);
    $result .= dig_get_dns_record("A", $host);
    $result .= dig_get_dns_record("CNAME", $host);
    return strpos($result, "ANSWER SECTION:") > 0;
}

While this will get me out of the woods it really isn't an answer. I am sure the real issue is a configuration setting on the Linux server.

If you are interested in testing the delay, here's a page with a few tests (the forms in the site suffer from this delay right now --please don't mess with the forms unless you actually just want to sign-up):

EDIT: link removed as it is no longer relevant and page will be deleted

Test suggestions:

apple.com
apple.commmmmmmmmmm
example.com
asdfasdfasdf

NB

AndreKR's answer caused me to test these functions with a trailing period and that alone reduced the response time from tens of seconds to milliseconds.

This solved the problem but it didn't really answer the new question: Why? For completeness I though it important to add this Nota Bene and answer that question based on my research.

I went back to the source and read most of RFC-1034 and RFC-1035.

It turns out that, as far as DNS is concerned, a Fully Qualified Domain Name (FQDN) actually ends with a period. Most references to FQDN do not explain that DNS has the concept of an absolute and relative domain names. The way DNS tell them apart is precisely by means of this trailing period.

If a DNS resolver sees a DNS-style FQDN (with a trailing period) it goes out and finds the requested records for that DNS FQDN (an absolute domain name specification). This might require a few retries. For example, if you are looking for an MX record and that does not exist in the FQDN record, a CNAME might exist instead. The DNS resolver grabs the CNAME and initiates a new DNS inquiry to try to find the MX record.

What happens if the DNS resolver encounters a relative domain name specification? In other words, anything without a trailing period. For example: "testing". The DNS resolver will actually try to turn this into an absolute FQDN by appending a series of DNS suffixes to the provided relative domain name. For example:

@dns_get_record("abceabce.gov", DNS_MX);  // No trailing period === relative

If run on the host, say, www.example.com will generate at least the following set of queries:

@dns_get_record("abceabce.gov.www.example.com", DNS_MX);
@dns_get_record("abceabce.gov.example.com", DNS_MX);
@dns_get_record("abceabce.gov.com", DNS_MX);

Each of these is likely to fail and the whole process takes forever.

It turns out that the whole process is actually covered in an RFC under A Security Problem and Proposed Correction With Widely Deployed DNS Software. It's worth reading.

like image 911
martin's Avatar asked Dec 28 '12 07:12

martin's


1 Answers

Apparently the PHP DNS functions don't have a timeout while the default timeout for dig is 5 seconds (with several tries).

Furthermore your development and production server probably use different DNS servers for resolving names. Now some DNS servers send an empty answer for invalid domains and some - like djbdns - don't send any answer at all (which is fine, according to the specification).

Also be aware that you should include the final . (dot) in the domain name if you don't want potential search domains from your resolv.conf/Windows network setup appended, which would be probably always resolvable if they have a wildcard.

like image 199
AndreKR Avatar answered Nov 14 '22 10:11

AndreKR