<?php
/* ------------------------------------------------------------------------
PHPresolver - PHP DNS resolver library
Version 1.1
Copyright (c) 2001, 2002 Moriyoshi Koizumi <koizumi@ave.sytes.net>
All Rights Reserved.
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or any
later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not,
write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
------------------------------------------------------------------------
*/
define( "DNS_RECORDTYPE_A", 1 );
define( "DNS_RECORDTYPE_NS", 2 );
define( "DNS_RECORDTYPE_CNAME", 5 );
define( "DNS_RECORDTYPE_SOA", 6 );
define( "DNS_RECORDTYPE_PTR", 12 );
define( "DNS_RECORDTYPE_MX", 15 );
define( "DNS_RECORDTYPE_AAAA", 28 );
define( "DNS_RECORDTYPE_ANY", 255 );
define( "DNS_RECORDTYPE_TXT", 16 );
/*
list of record types not yet implemented
define( "DNS_RECORDTYPE_A6", 38 );
define( "DNS_RECORDTYPE_NULL", 10 );
define( "DNS_RECORDTYPE_OPT", 41 );
define( "DNS_RECORDTYPE_TKEY", 249 );
define( "DNS_RECORDTYPE_TSIG", 250 );
define( "DNS_RECORDTYPE_IXFR", 251 );
define( "DNS_RECORDTYPE_AXFR", 252 );
define( "DNS_RECORDTYPE_MAILB", 253 );
define( "DNS_RECORDTYPE_MAILA", 254 );
define( "DNS_RECORDTYPE_MD", 3 );
define( "DNS_RECORDTYPE_MF", 4 );
define( "DNS_RECORDTYPE_MB", 7 );
define( "DNS_RECORDTYPE_MG", 8 );
define( "DNS_RECORDTYPE_MR", 9 );
define( "DNS_RECORDTYPE_WKS", 11 );
define( "DNS_RECORDTYPE_HINFO", 13 );
define( "DNS_RECORDTYPE_MINFO", 14 );
define( "DNS_RECORDTYPE_RP", 17 );
define( "DNS_RECORDTYPE_AFSDB", 18 );
define( "DNS_RECORDTYPE_X25", 19 );
define( "DNS_RECORDTYPE_ISDN", 20 );
define( "DNS_RECORDTYPE_RT", 21 );
define( "DNS_RECORDTYPE_NSAP", 22 );
define( "DNS_RECORDTYPE_NSAP_PTR", 23 );
define( "DNS_RECORDTYPE_SIG", 24 );
define( "DNS_RECORDTYPE_KEY", 25 );
define( "DNS_RECORDTYPE_PX", 26 );
define( "DNS_RECORDTYPE_GPOS", 27 );
define( "DNS_RECORDTYPE_LOC", 29 );
define( "DNS_RECORDTYPE_NXT", 30 );
define( "DNS_RECORDTYPE_EID", 31 );
define( "DNS_RECORDTYPE_NIMLOC", 32 );
define( "DNS_RECORDTYPE_SRV", 33 );
define( "DNS_RECORDTYPE_ATMA", 34 );
define( "DNS_RECORDTYPE_NAPTR", 35 );
define( "DNS_RECORDTYPE_KX", 36 );
define( "DNS_RECORDTYPE_CERT", 37 );
*/
define( "DNS_OPCODE_QUERY" , 0x0000 );
define( "DNS_OPCODE_IQUERY", 0x0800 );
define( "DNS_OPCODE_STATUS", 0x1000 );
define( "DNS_OPCODE_NOTIFY", 0x1800 );
define( "DNS_OPCODE_UPDATE", 0x2000 );
define( "DNS_RCODE_SUCCESSFUL" , 0x0001 );
define( "DNS_RCODE_MALFORMATED" , 0x0002 );
define( "DNS_RCODE_FAILEDSERVER" , 0x0003 );
define( "DNS_RCODE_NAMEERROR" , 0x0004 );
define( "DNS_RCODE_UNIMPLEMENTED", 0x0005 );
define( "DNS_RCODE_REFUSED" , 0x0006 );
define( "DNS_HEADERSPEC_IS_RESPONSE" , 0x8000 );
define( "DNS_HEADERSPEC_OPCODE_MASK" , 0x7800 );
define( "DNS_HEADERSPEC_AUTHORITIVE_ANSWER" , 0x0400 );
define( "DNS_HEADERSPEC_TRUNCATED" , 0x0200 );
define( "DNS_HEADERSPEC_RECURSION_DESIRED" , 0x0100 );
define( "DNS_HEADERSPEC_RECURSION_AVAILABLE" , 0x0080 );
define( "DNS_HEADERSPEC_RESPONSE_SPEC_MASK" , 0x0480 );
define( "DNS_HEADERSPEC_QUERY_SPEC_MASK" , 0x0300 );
define( "DNS_HEADERSPEC_RESERVED" , 0x0e00 );
define( "DNS_HEADERSPEC_RESULT_CODE_MASK" , 0x000f );
define( "DNS_CLASS_INTERNET" , 0x0001 );
define( "DNS_UDP_PACKET_MAX_LENGTH", 512 );
class DNSRecord
{
var $type;
var $name;
var $dclass;
var $ttl;
var $specific_fields;
function DNSRecord(
$name,
$type,
$dclass = DNS_CLASS_INTERNET,
$ttl = 0,
$specific_fields = false ) {
$this->name = $name;
$this->type = $type;
$this->dclass = $dclass;
$this->ttl = $ttl;
$this->specific_fields = $specific_fields;
}
function &getTypeSpecificField( $name ) {
if( $this->specific_fields ) {
return $this->specific_fields[$name];
}
return false;
}
}
class DNSResolver
{
var $port;
var $nameserver;
var $timeout;
function DNSResolver( $nameserver, $port = 53, $timeout = 1000000 ) {
$this->port = $port;
$this->nameserver = $nameserver;
$this->timeout = $timeout;
}
function sendQuery( $dnsquery, $useTCP = false ) {
$answer = false;
$out_buf = $dnsquery->asOctets( false );
$out_buf_len = strlen( $out_buf );
if( $out_buf ) {
if( $useTCP == false && $out_buf_len <= DNS_UDP_PACKET_MAX_LENGTH ) {
/* connection by UDP */
if( ( $sock = fsockopen( 'udp://'.$this->nameserver, $this->port, $this->timeout ) ) === false ) {
return false;
}
socket_set_blocking( $sock, true );
if( fwrite( $sock, $out_buf ) == $out_buf_len ) {
$answer = new DNSAnswer( $sock, DNS_UDP_PACKET_MAX_LENGTH );
}
fclose( $sock );
} else {
/* connection by TCP */
if( ( $sock = fsockopen( $this->nameserver, $this->port, $this->timeout ) ) === false ) {
return false;
}
socket_set_blocking( $sock, true );
if( fwrite( $sock, pack( 'n', $out_buf_len ) ) == 2 &&
fwrite( $sock, $out_buf ) == $out_buf_len ) {
$tmp = unpack( 'nl', fread( $sock, 2 ) );
$limit_length = $tmp['l'];
print $limit_length;
$answer = new DNSAnswer( $sock, $limit_length );
}
fclose( $sock );
}
}
return $answer;
}
}
class DNSQuery
{
var $id; // 1 - 65535
var $header_opcode;
var $query_record;
var $flags;
function DNSQuery( &$dnsrecord, $flags = DNS_HEADERSPEC_RECURSION_DESIRED )
{
$this->id = rand( 1, 255 ) | ( rand( 0, 255 ) << 8 );
$this->flags = $flags & DNS_HEADERSPEC_QUERY_SPEC_MASK;
$this->header_opcode = DNS_OPCODE_QUERY;
$this->query_record = &$dnsrecord;
}
function asOctets() {
if( $this->query_record->name === false ) { return false; }
$buf = '';
$buf .= pack( "nnnnnn", $this->id, DNS_OPCODE_QUERY | $this->flags, 1, 0, 0, 0 );
$buf .= $this->query_record->name->asOctets();
$buf .= pack( "nn", $this->query_record->type, $this->query_record->dclass );
return $buf;
}
}
class DNSMessageParser
{
var $stream;
var $nbytes_read;
var $octets;
var $limit;
function DNSMessageParser( $stream, $limit ) {
$this->stream = $stream;
$this->nbytes_read = 0;
$this->octets = '';
$this->limit = $limit;
}
function readStreamDirectly( $nbytes ) {
if( ( $this->limit -= $nbytes ) < 0 ) {
return false;
}
$buf = fread( $this->stream, $nbytes );
$this->octets .= $buf;
$this->nbytes_read += $nbytes;
return $buf;
}
function readBufferedStream( $nbytes, $offset ) {
if( $offset < $this->nbytes_read ) {
$diff = $this->nbytes_read - $offset;
if( $nbytes <= $diff ) {
return substr( $this->octets, $offset, $nbytes );
} else {
$buf = substr( $this->octets, $offset, $diff );
$nbytes -= $diff;
}
} else {
$buf = '';
while( $offset > $this->nbytes_read ) {
if( $this->readStreamDirectly( $offset - $this->nbytes_read ) === false ) {
return false;
}
}
}
if( ( $_buf = $this->readStreamDirectly( $nbytes ) ) === false ) {
return false;
}
$buf .= $_buf;
return $buf;
}
function getHeaderInfo() {
if( ( $buf = $this->readStreamDirectly( 12 ) ) === false ) {
$this = false;
return;
}
return unpack( "nid/nspec/nqdcount/nancount/nnscount/narcount", $buf );
}
function getQueryRecords( $nrecs ) {
$recs = array();
while( --$nrecs >= 0 ) {
if( ( $labels = $this->getLabels() ) === false ) {
$this = false;
return;
}
if( ( $buf = $this->readStreamDirectly( 4 ) ) === false ) {
$this = false;
return;
}
$info = unpack( "ntype/ndclass", $buf );
$recs[] = new DNSRecord(
new DNSName( $labels ),
$info['type'],
$info['dclass']
);
}
return $recs;
}
function getResourceRecords( $nrecs ) {
$recs = array();
while( --$nrecs >= 0 ) {
if( ( $labels = $this->getLabels() ) === false ) {
return false;
}
if( ( $buf = $this->readStreamDirectly( 10 ) ) === false ) {
return false;
}
$info = unpack( "ntype/ndclass/Nttl/nrdlength", $buf );
switch( $info['type'] ) {
case DNS_RECORDTYPE_CNAME:
case DNS_RECORDTYPE_NS:
case DNS_RECORDTYPE_PTR:
if( ($_labels = $this->getLabels($info['rdlength']) ) === false ) {
return false;
}
$specific_fields = array( 'dname' => new DNSName( $_labels ) );
break;
case DNS_RECORDTYPE_TXT:
if( ($_labels = $this->getLabels($info['rdlength']) ) === false ) {
return false;
}
$specific_fields = array( 'text' => $_labels );
break;
case DNS_RECORDTYPE_MX:
if( ( $buf = $this->readStreamDirectly(2) ) === false ) {
return false;
}
$specific_fields = unpack( 'npreference', $buf );
if( ( $_labels = $this->getLabels($info['rdlength']-2) ) === false ) {
return false;
}
$specific_fields['exchange'] = new DNSName( $_labels );
break;
case DNS_RECORDTYPE_A:
if( ( $buf = $this->readStreamDirectly(4) ) === false ) {
return false;
}
$specific_fields = array( 'address' => DNSName::newFromString( implode( '.', unpack( 'Ca/Cb/Cc/Cd', $buf ) ) ) );
break;
case DNS_RECORDTYPE_AAAA:
if( ( $buf = $this->readStreamDirectly(16) ) === false ) {
return false;
}
$specific_fields = array( 'address' => DNSName::newFromString( implode( '.', unpack( 'Ca/Cb/Cc/Cd/Ce/Cf/Cg/Ch/Ci/Cj/Ck/Cl/Cm/Cn/Co/Cp', $buf ) ).'.IP6.ARPA' ) );
break;
case DNS_RECORDTYPE_SOA:
$specific_fields = array();
if( ($_labels = $this->getLabels($info['rdlength']) ) === false ) {
return false;
}
$specific_fields['source'] = new DNSName( $_labels );
if( ($_labels = $this->getLabels($info['rdlength']) ) === false ) {
return false;
}
$specific_fields['resp_person'] = array_shift( $_labels ).'@';
$specific_fields['resp_person'] .= implode( '.', $_labels );
if( ( $buf = $this->readStreamDirectly(20) ) === false ) {
return false;
}
$specific_fields = array_merge(
$specific_fields,
unpack( 'Nserial/Nrefresh/Nretry/Nexpire/Nminttl', $buf )
);
break;
default:
if( $this->readStreamDirectly( $info['rdlength'] ) === false ) {
return false;
}
$specific_fields = false;
}
$recs[] = new DNSRecord(
new DNSName( $labels ),
$info['type'],
$info['dclass'],
$info['ttl'],
$specific_fields
);
}
return $recs;
}
function getLabels( $max_length = 255, $offset = -1 ) {
if( $offset < 0 ) { $offset = $this->nbytes_read; }
$labels = array();
for(;;) {
if( --$max_length < 0 ) { return false; }
if( ( $buf = $this->readBufferedStream( 1, $offset ) ) === false ) {
return false;
}
$label_len = ord( $buf );
++$offset;
if( $label_len < 64 ) {
/* uncompressed */
if( ( $max_length -= $label_len ) < 0 ) { return false; }
if( ( $labels[] = $this->readBufferedStream( $label_len, $offset ) ) === false ) {
return false;
}
$offset += $label_len;
if( $label_len == 0 ) { break; }
} else {
/* compressed */
if( ( $buf = $this->readBufferedStream( 1, $offset ) ) === false ) {
return false;
}
if( --$max_length < 0 ) {
return false;
}
$_offset = ( ( $label_len & 0x3f ) << 8 ) + ord( $buf );
if( ($_labels = $this->getLabels( $offset - $_offset, $_offset )) === false ) {
return false;
}
$labels = array_merge( $labels, $_labels );
break;
}
}
return $labels;
}
}
class DNSAnswer
{
var $id;
var $result_code;
var $flags;
var $rec_query;
var $rec_answer;
var $rec_authority;
var $rec_additional;
function DNSAnswer( &$stream, $limit ) {
$msgparser = new DNSMessageParser( $stream, $limit );
$info = & $msgparser->getHeaderInfo();
$this->id = $info['id'];
$this->result_code = $info['spec'] & DNS_HEADERSPEC_RESULT_CODE_MASK;
$this->flags = $info['spec'] & DNS_HEADERSPEC_RESPONSE_SPEC_MASK;
$nrec_query = $info['qdcount'];
$nrec_answer = $info['ancount'];
$nrec_authority = $info['nscount'];
$nrec_additional = $info['arcount'];
if( ( $this->rec_query = &$msgparser->getQueryRecords( $nrec_query ) ) === false ) {
$this = false;
return;
}
if( ( $this->rec_answer = &$msgparser->getResourceRecords( $nrec_answer ) ) === false ) {
$this = false;
return;
}
if( ( $this->rec_authority = &$msgparser->getResourceRecords( $nrec_authority ) ) === false ) {
$this = false;
return;
}
if( ( $this->rec_additional = &$msgparser->getResourceRecords( $nrec_additional ) ) === false ) {
$this = false;
return;
}
}
}
class DNSName
{
var $labels;
function DNSName( $labels ) {
$this->labels = & $labels;
}
function isRealDomainName() {
$i = count( $this->labels ) - 1;
if( $i >= 1 && strtoupper($this->labels[$i-1]) == 'ARPA' ) {
return false;
}
return true;
}
function newFromString( $domain_name ) {
if( strpos( $domain_name, ':' ) !== false && !ereg( '[^0-9a-fA-f:.]', $domain_name ) ) {
/* IPv6 address literal expression spec */
$labels = array();
$components = explode( ':', $domain_name );
$ncomponents = count($components);
$offset = $ncomponents;
while( --$offset >= 0 ) {
$subcomps = explode( '.', $components[$offset] );
$nsubcomps = count( $subcomps );
if( $nsubcomps == 1 ) {
if( $subcomps[0] == '' ) {
$_offset = 0;
while( $components[$_offset] != '' ) {
++$_offset;
}
$count = 9-($ncomponents-$offset)+$_offset;
while( --$count >= 0 ) {
$labels[] = '0';
$labels[] = '0';
$labels[] = '0';
$labels[] = '0';
}
if( $_offset < $offset ) {
$offset = $_offset;
}
} else {
$compval = hexdec( $subcomps[0] );
$labels[] = dechex( $compval & 0x0f );
$compval >>= 4;
$labels[] = dechex( $compval & 0x0f );
$compval >>= 4;
$labels[] = dechex( $compval & 0x0f );
$compval >>= 4;
$labels[] = dechex( $compval & 0x0f );
$compval >>= 4;
}
} elseif( $nsubcomps == 4 ) {
$labels[] = dechex( $subcomps[3] );
$labels[] = dechex( $subcomps[2] );
$labels[] = dechex( $subcomps[1] );
$labels[] = dechex( $subcomps[0] );
} else {
return false;
}
}
$labels[] = 'IP6';
$labels[] = 'ARPA';
$labels[] = '';
} else {
if( substr( $domain_name, -1, 1 ) != '.' ) {
$domain_name .= '.';
}
$labels = explode( '.', $domain_name );
$nlabels = count( $labels );
if( $nlabels == 5 && !ereg( '[^0-9.]', $domain_name ) ) {
/* IPv4 raw address literal representation spec */
$tmp = (string)$labels[0];
$labels[0] = (string)$labels[3];
$labels[3] = $tmp;
$tmp = (string)$labels[1];
$labels[1] = (string)$labels[2];
$labels[2] = $tmp;
$labels[4] = 'IN-ADDR';
$labels[5] = 'ARPA';
$labels[6] = '';
}
}
return new DNSName( $labels );
}
function asOctets() {
$upto = count( $this->labels );
$buf = '';
for( $offset = 0; $offset < $upto; ++$offset ) {
$label_len = strlen( $this->labels[$offset] );
$buf .= pack( "C", $label_len ).$this->labels[$offset];
}
return $buf;
}
function getCanonicalName() {
return implode( ".", $this->labels );
}
}
?>
|