diff --git a/DNS/dnsProtocol.php b/DNS/dnsProtocol.php index bff5e5a..6fae3bc 100644 --- a/DNS/dnsProtocol.php +++ b/DNS/dnsProtocol.php @@ -28,9 +28,19 @@ class dnsProtocol protected $port; /** * - * @var float $timeout = 60 + * @var float $connectiontimeout = 60 */ - protected $timeout; + protected $connectiontimeout; + /** + * + * @var float $readtimeoutsec = 60 + */ + protected $readtimeoutsec; + /** + * + * @var float $readtimeoutusec = 0 + */ + protected $readtimeoutusec; /** * * @var boolean $udp = false; @@ -42,7 +52,8 @@ class dnsProtocol */ protected $types; - const DEFAULT_TIMEOUT = 60; + const DEFAULT_CONNECTION_TIMEOUT = 60.0; + const DEFAULT_READ_TIMEOUT = 60.0; function __construct($logging = false) { @@ -51,10 +62,10 @@ function __construct($logging = false) $this->enableLogging(); } $this->port=53; - $this->timeout=self::DEFAULT_TIMEOUT; + $this->setConnectionTimeout(); + $this->setReadTimeout(); $this->udp=false; $this->types=new dnsTypes(); - set_error_handler(array($this,'error_handler')); $this->writelog("dnsProtocol Class Initialised"); } @@ -85,6 +96,9 @@ function error_handler($errno = 0, $errstr = null, $errfile = null, $errline = n */ function Query($question,$type='A') { + set_error_handler(array($this,'error_handler')); + try { + $typeid=$this->types->GetByName($type); if ($typeid===false) { @@ -99,7 +113,7 @@ function Query($question,$type='A') { $host=$this->server; } - if (!$socket=@fsockopen($host,$this->port,$errno,$errstr,$this->timeout)) + if (!$socket=@fsockopen($host,$this->port,$errno,$errstr,$this->connectiontimeout)) { throw new dnsException("Failed to open socket to ".$host); } @@ -164,10 +178,21 @@ function Query($question,$type='A') fclose($socket); throw new dnsException("Failed to write question to socket"); } - if (!$this->rawbuffer=fread($socket,4096)) // read until the end with UDP + + stream_set_blocking($socket, true); // set blocking mode to make fread wait for data to become available + stream_set_timeout($socket, $this->readtimeoutsec, $this->readtimeoutusec); // set read timeout + + $this->rawbuffer=fread($socket,4096); + $info = stream_get_meta_data($socket); + if ($info['timed_out']) { fclose($socket); - throw new dnsException("Failed to write read data buffer"); + throw new dnsException("Connection timed out while read data buffer"); + } + if (!$this->rawbuffer) // read until the end with UDP + { + fclose($socket); + throw new dnsException("Failed to read data buffer"); } } else // TCP method @@ -182,14 +207,32 @@ function Query($question,$type='A') fclose($socket); throw new dnsException("Failed to write question to TCP socket"); } - if (!$returnsize=fread($socket,2)) + + stream_set_blocking($socket, true); // set blocking mode to make fread wait for data to become available + stream_set_timeout($socket, $this->readtimeoutsec, $this->readtimeoutusec); // set read timeout + + $returnsize=fread($socket,2); + $info = stream_get_meta_data($socket); + if ($info['timed_out']) + { + fclose($socket); + throw new dnsException("Connection timed out"); + } + if (!$returnsize) { fclose($socket); } $tmplen=unpack("nlength",$returnsize); $datasize=$tmplen['length']; $this->writeLog("TCP Stream Length Limit ".$datasize); - if (!$this->rawbuffer=fread($socket,$datasize)) + $this->rawbuffer=fread($socket,$datasize); + $info = stream_get_meta_data($socket); + if ($info['timed_out']) + { + fclose($socket); + throw new dnsException("Connection timed out while read data buffer"); + } + if (!$this->rawbuffer) { fclose($socket); throw new dnsException("Failed to read data buffer"); @@ -260,6 +303,10 @@ function Query($question,$type='A') $response->ReadRecord($this->rawbuffer,dnsResponse::RESULTTYPE_ADDITIONAL); } return $response; + + } finally { + restore_error_handler(); + } } public function setServer($server) @@ -272,18 +319,39 @@ public function getServer() return $this->server; } - public function setTimeout($timeout = self::DEFAULT_TIMEOUT) + /** + * @param float $timeout - integer part means seconds, fractional part means microseconds + * @throws dnsException + */ + public function setConnectionTimeout($timeout = self::DEFAULT_CONNECTION_TIMEOUT) { - if (!$new_timeout = floatval($timeout) || $timeout < 0) - { - throw new dnsException("Incorrect timeout value: <" . $timeout . ">. Timeout must be positive number."); + if (!is_float($timeout) || $timeout <= 0) { + throw new dnsException("Invalid connection timeout value: < $timeout >. Timeout must be a positive float."); + } + $this->connectiontimeout = $timeout; + } + + public function getConnectionTimeout() + { + return $this->connectiontimeout; + } + + /** + * @param float $timeout - integer part means seconds, fractional part means microseconds + * @throws dnsException + */ + public function setReadTimeout($timeout = self::DEFAULT_READ_TIMEOUT) + { + if (!is_float($timeout) || $timeout <= 0) { + throw new dnsException("Invalid connection timeout value: < $timeout >. Timeout must be a positive float."); } - $this->timeout = $new_timeout; + $this->readtimeoutsec = (int)floor($timeout); // get seconds + $this->readtimeoutusec = (int)round(($timeout - $this->readtimeoutsec) * 1000000); // get microseconds } - public function getTimeout() + public function getReadTimeout() { - return $this->timeout; + return $this->readtimeoutsec + $this->readtimeoutusec / 1000000; } public function setPort($port) @@ -358,8 +426,13 @@ function algorithm($code) function registrynameservers($tld) { - $ns = new dnsNameserver(); - return $ns->getNs($tld); + set_error_handler(array($this,'error_handler')); + try { + $ns = new dnsNameserver(); + return $ns->getNs($tld); + } finally { + restore_error_handler(); + } } function base32encode($input, $padding = true) {