2

A tool I am creating enables users to modify their reversed DNS records. Since these records can also be modified through external methods, it is imperative that I can show the users their actual current reverse DNS records.

As it stands both gethostbyaddr($ip) aswell as dns_get_record(<reversed ip>.in-addr.arpa) return cached records after the first request.

Basically I need to query the authorative nameserver (which, in my case, is a known server/ip) directly, so that caching is not an issue on both the server as well as the non-authorative nameservers it uses).

So the actual question (also TL;DR): How can I query a specific (authorative) nameserver in the most efficient way using native PHP methods. (php 5.3+)

Before marking this question as a duplicate: all existing answers on similar questions point to PEAR's NET2 class. This question seeks the answer if there is an easier way to do this without external classes ie. by a new method/class (newer versions of PHP)

Damien Overeem
  • 4,487
  • 4
  • 36
  • 55

1 Answers1

2

I have found some code on the PHP.net docs for gethostbyaddr() that seems to make a udp connection to a specific DNS server (you give it the IP), and then find the hostname of a specific IP. Have changed it a bit so it looks like the code you want. Maybe it is useful.

<?php
echo gethostbyaddr_custom('173.194.32.37','8.8.8.8');

function gethostbyaddr_custom($ip, $dns){
    // random transaction number (for routers etc to get the reply back)
    $data = rand(0, 99);
    // trim it to 2 bytes
    $data = substr($data, 0, 2);
    // request header
    $data .= "\1\0\0\1\0\0\0\0\0\0";
    // split IP up
    $bits = explode(".", $ip);
    // error checking
    if (count($bits) != 4) return "ERROR";
    // there is probably a better way to do this bit...
    // loop through each segment
    for ($x=3; $x>=0; $x--)
    {
        // needs a byte to indicate the length of each segment of the request
        switch (strlen($bits[$x]))
        {
            case 1: // 1 byte long segment
                $data .= "\1"; break;
            case 2: // 2 byte long segment
                $data .= "\2"; break;
            case 3: // 3 byte long segment
                $data .= "\3"; break;
            default: // segment is too big, invalid IP
                return "INVALID";
        }
        // and the segment itself
        $data .= $bits[$x];
    }
    // and the final bit of the request
    $data .= "\7in-addr\4arpa\0\0\x0C\0\1";
    // create UDP socket
    $handle = @fsockopen("udp://$dns", 53);
    // send our request (and store request size so we can cheat later)
    $requestsize=@fwrite($handle, $data);

    $response = @fread($handle, 1000);
    @fclose($handle);
    if ($response == "")
        return $ip;
    // find the response type
    $type = @unpack("s", substr($response, $requestsize+2));
    if ($type[1] == 0x0C00)  // answer
    {
        // set up our variables
        $host="";
        $len = 0;
        // set our pointer at the beginning of the hostname
        // uses the request size from earlier rather than work it out
        $position=$requestsize+12;
        // reconstruct hostname
        do
        {
            // get segment size
            $len = unpack("c", substr($response, $position));
            // null terminated string, so length 0 = finished
            if ($len[1] == 0)
                // return the hostname, without the trailing .
                return substr($host, 0, strlen($host) -1);
            // add segment to our host
            $host .= substr($response, $position+1, $len[1]) . ".";
            // move pointer on to the next segment
            $position += $len[1] + 1;
        }
        while ($len != 0);
        // error - return the hostname we constructed (without the . on the end)
        return $ip;
    }
    return $ip;
}
?>
Trolley
  • 2,328
  • 2
  • 23
  • 28