Wow. where queries must be fast and only consist of a single request followed by a single reply packet

  1. User_Datagram_Protocol
  2. Domain_Name_System
  3. http://technet.microsoft.com/en-us/library/dd197470%28v=ws.10%29.aspx
  4. http://tools.ietf.org/html/rfc1035
  5. http://perplexed.co.uk/948_dns_message_format.htm
  6. http://pear.php.net/package/Net_DNS2/
Reverse lookup A reverse lookup is a query of the DNS for domain names when the IP address is known. Multiple domain names may be associated with an IP address. The DNS stores IP addresses in the form of domain names as specially formatted names in pointer (PTR) records within the infrastructure top-level domain arpa. For IPv4, the domain is in-addr.arpa. For IPv6, the reverse lookup domain is ip6.arpa. The IP address is represented as a name in reverse-ordered octet representation for IPv4, and reverse-ordered nibble representation for IPv6. When performing a reverse lookup, the DNS client converts the address into these formats before querying the name for a PTR record following the delegation chain as for any DNS query. For example, assuming the IPv4 address 208.80.152.2 is assigned to Wikimedia, it is represented as a DNS name in reverse order: 2.152.80.208.in-addr.arpa. When the DNS resolver gets a pointer (PTR) request, it begins by querying the root servers, which point to the servers of American Registry for Internet Numbers (ARIN) for the 208.in-addr.arpa zone. ARIN's servers delegate 152.80.208.in-addr.arpa to Wikimedia to which the resolver sends another query for 2.152.80.208.in-addr.arpa, which results in an authoritative response.
<?php

function get_hostbyaddr( $ip, $dns = '8.8.8.8', $timeout = 1 ) {
  ISCLOG::ti($ip);
  
  static $ips = array();
  
  if ( isset( $ips[ $ip ] ) ) return $ips[ $ip ];
  else $ips[ $ip ] = $ip;
  
  // random transaction number (for routers etc to get the reply back)
  $tx = rand( 10, 77 ) . "\1\0\0\1\0\0\0\0\0\0";
  
  // octals in the array, keys are strlen of bit
  $bitso = array( "", "\1", "\2", "\3" );
  $arpa = '';
  foreach( array_reverse( explode( '.', $ip ) ) as $bit ) {
    $arpa .= $bit . '.';
    $l = strlen( $bit );
    $tx .= "{$bitso[$l]}" . $bit;
  }
  $arpa .= 'in-addr.arpa';
  
  // and the final bit of the request
  $tx .= "\7in-addr\4arpa\0\0\x0C\0\1";

  // create UDP socket
  $errno = $errstr = 0;
  $fp = fsockopen( "udp://{$dns}", 53, $errno, $errstr, 0.5 );
  if( ! $fp || ! is_resource( $fp ) ) return ( $ips[ $ip ] = $ip );
  
  
  if( function_exists( 'socket_set_timeout' ) ) {
    socket_set_timeout( $fp, $timeout );
  } elseif ( function_exists( 'stream_set_timeout' ) ) {
    stream_set_timeout( $fp, $timeout );
  }
  
  

  // send our request (and store request size so we can cheat later)
  $tx_size = fwrite( $fp, $tx );
  $max_rx = $tx_size * 7;
  
  $start = time();
  $rx_size = $res_len = $stop_at = 0;
  $rx = '';
  
  while ( 
      (
        ( $stop_at > 0 && $rx_size < $stop_at ) || $stop_at == 0
      )
      && ! feof( $fp )
      && $rx_size < $max_rx
      && ( ( time() - $start ) < $timeout )
      && ($b = fread( $fp, 1 ) ) !== false
  ) {
    $rx_size++;
    $rx .= $b;

    if ( $stop_at == 0 && $res_len == 0 && ( $rx_size > $tx_size + 12 ) ) {
      $res_len = hexdec( bin2hex( substr( $rx, $tx_size + 11, 1 ) ) );
      $stop_at = ( $res_len + $tx_size + 12 );
    }
  }
  
  $response_length = hexdec( bin2hex( substr( $rx, $tx_size + 11, 1 ) ) );
  /*
  echo "[response_length]: {$response_length}  C:" .   bin2hex( substr( $rx, $tx_size + $response_length + 11, 1 )) . "  B:" . bin2hex( substr( $rx, -1 ) ) . "\n";
  echo "[tx: $tx_size bytes]  \n" . ISCLOG::hexdump($tx,array('ascii_len'=>50))."\n";
  echo "[rx: {$rx_size} bytes]\n" . ISCLOG::hexdump($rx,array('ascii_len'=>50))."\n";
  */

  // hope we get a reply
  if ( is_resource( $fp ) ) fclose( $fp );

  // if empty response or bad response, return original ip
  if ( empty( $rx ) || bin2hex( substr( $rx, $tx_size + 2, 2 ) ) != '000c' || bin2hex( substr( $rx, -1 ) ) != '00' ) {
    //$hostip = @gethostbyname( $ip );

    echo "!!! [{$ip}  - tx:{$tx_size} rx:{$rx_size} stop:{$stop_at} res_len:{$res_len} ]  " . 
    " 000c:" . bin2hex( substr( $rx, $tx_size + 2, 2 ) ) . " 00:" . bin2hex( substr( $rx, -1 ) ) . "\n";
    //echo ISCLOG::hexdump($rx,array('ascii_len'=>50))."\n";
    //echo ISCLOG::hexdump($tx,array('ascii_len'=>50))."\n" . ISCLOG::hexdump($rx,array('ascii_len'=>50))."\n";
    //$ip = ( $hostip == $ip ) ? $ip : long2ip( ip2long( $ip ) );
  
    return ( $ips[ $ip ] = $ip );
  }
    
  // set up our variables
  $host = '';
  $len = $loops = 0;
  
  // set our pointer at the beginning of the hostname uses the request size from earlier rather than work it out
  $pos = $tx_size + 12;
  do {
    $myc = substr( $rx, $pos, 1 );
    
    // get segment size
    if ( strlen( $myc ) > 0 ) $len = unpack( 'c', $myc );
    
    // null terminated string, so length 0 = finished - return the hostname, without the trailing .
    if ( $len[1] == 0 ) return ( $ips[ $ip ] = substr( $host, 0, -1 ) );
    
    // add segment to our host
    $host .= substr( $rx, $pos + 1, abs( $len[1] ) ) . '.';
    //ISCLOG::l($loops . ': '. 'pos:'.$pos. ' len:' . $len[1] . ' ' . $host);

    // move pointer on to the next segment
    $pos += $len[1] + 1;
    
  } while ( $len[1] != 0 && $loops++ < 40 );

  // return the ip in case 
  return ( $ips[ $ip ] = $ip );
}

Tags

Comments