Viewing file: DNS2.php (26.7 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** * DNS Library for handling lookups and updates. * * PHP Version 5 * * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of Mike Pultz nor the names of his contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @category Networking * @package Net_DNS2 * @author Mike Pultz <mike@mikepultz.com> * @copyright 2010 Mike Pultz <mike@mikepultz.com> * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id: DNS2.php 137 2011-12-05 02:51:34Z mike.pultz $ * @link http://pear.php.net/package/Net_DNS2 * @since File available since Release 0.6.0 * */
/* * register the auto-load function * */ spl_autoload_register('Net_DNS2::autoload');
/** * This is the base class for the Net_DNS2_Resolver and Net_DNS2_Updater * classes. * * @category Networking * @package Net_DNS2 * @author Mike Pultz <mike@mikepultz.com> * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @link http://pear.php.net/package/Net_DNS2 * @see Net_DNS2_Resolver, Net_DNS2_Updater * */ class Net_DNS2 { /* * the current version of this library */ const VERSION = '1.2.0';
/* * the default path to a resolv.conf file */ const RESOLV_CONF = '/etc/resolv.conf';
/* * use TCP only (true/false) */ public $use_tcp = false;
/* * DNS Port to use (53) */ public $dns_port = 53;
/* * the ip/port for use as a local socket */ public $local_host = ''; public $local_port = 0;
/* * timeout value for socket connections */ public $timeout = 5;
/* * randomize the name servers list */ public $ns_random = false;
/* * default domains */ public $domain = '';
/* * domain search list - not actually used right now */ public $search_list = array();
/* * enable cache; either "shared", "file" or "none" */ public $cache_type = 'none';
/* * file name to use for shared memory segment or file cache */ public $cache_file = '/tmp/net_dns2.cache';
/* * the max size of the cache file (in bytes) */ public $cache_size = 10000;
/* * the method to use for storing cache data; either "serialize" or "json" * * json is faster, but can't remember the class names (everything comes back as a * "stdClass Object"; all the data is the same though. serialize is slower, but will * have all the class info. * * defaults to 'serialize' */ public $cache_serializer = 'serialize';
/* * local sockets */ protected $sock = array('udp' => array(), 'tcp' => array());
/* * name server list */ protected $nameservers = array();
/* * if the socket extension is loaded */ protected $sockets_enabled = false;
/* * the TSIG or SIG RR object for authentication */ protected $auth_signature = null;
/* * the shared memory segment id for the local cache */ protected $cache = null;
/* * internal setting for enabling cache */ protected $use_cache = false;
/* * the last erro message returned by the sockets class */ private $_last_socket_error = '';
/** * Constructor - base constructor for the Resolver and Updater * * @param mixed $options array of options or null for none * * @throws Net_DNS2_Exception * @access public * */ public function __construct(array $options = null) { // // check for the sockets extension // $this->sockets_enabled = extension_loaded('sockets');
// // load any options that were provided // if (!empty($options)) {
foreach ($options as $key => $value) {
if ($key == 'nameservers') {
$this->setServers($value); } else {
$this->$key = $value; } } }
// // if we're set to use the local shared memory cache, then // make sure it's been initialized // switch($this->cache_type) { case 'shared': if (extension_loaded('shmop')) {
$this->cache = new Net_DNS2_Cache_Shm; $this->use_cache = true; } else {
throw new Net_DNS2_Exception( 'shmop library is not available for cache', Net_DNS2_Lookups::E_CACHE_SHM_UNAVAIL ); } break; case 'file':
$this->cache = new Net_DNS2_Cache_File; $this->use_cache = true;
break; case 'none': $this->use_cache = false; break; default:
throw new Net_DNS2_Exception( 'un-supported cache type: ' . $this->cache_type, Net_DNS2_Lookups::E_CACHE_UNSUPPORTED ); } }
/** * autoload call-back function; used to auto-load classes * * @param string $name the name of the class * * @return void * @access public * */ static public function autoload($name) { // // only auto-load our classes // if (strncmp($name, 'Net_DNS2', 8) == 0) {
include str_replace('_', '/', $name) . '.php'; }
return; }
/** * sets the name servers to be used * * @param mixed $nameservers either an array of name servers, or a file name * to parse, assuming it's in the resolv.conf format * * @return boolean * @throws Net_DNS2_Exception * @access public * */ public function setServers($nameservers) { // // if it's an array, then use it directly // // otherwise, see if it's a path to a resolv.conf file and if so, load it // if (is_array($nameservers)) {
$this->nameservers = $nameservers;
} else {
// // check to see if the file is readable // if (is_readable($nameservers) === true) { $data = file_get_contents($nameservers); if ($data === false) { throw new Net_DNS2_Exception( 'failed to read contents of file: ' . $nameservers, Net_DNS2_Lookups::E_NS_INVALID_FILE ); }
$lines = explode("\n", $data);
foreach ($lines as $line) { $line = trim($line);
// // ignore empty lines, and lines that are commented out // if ( (strlen($line) == 0) || ($line[0] == '#') || ($line[0] == ';') ) { continue; }
list($key, $value) = preg_split('/\s+/', $line, 2);
$key = trim(strtolower($key)); $value = trim(strtolower($value));
switch($key) { case 'nameserver':
// // nameserver can be a IPv4 or IPv6 address // if ( (self::isIPv4($value) == true) || (self::isIPv6($value) == true) ) {
$this->nameservers[] = $value; } else {
throw new Net_DNS2_Exception( 'invalid nameserver entry: ' . $value, Net_DNS2_Lookups::E_NS_INVALID_ENTRY ); } break;
case 'domain': $this->domain = $value; break;
case 'search': $this->search_list = preg_split('/\s+/', $value); break;
default: ; } }
// // if we don't have a domain, but we have a search list, then // take the first entry on the search list as the domain // if ( (strlen($this->domain) == 0) && (count($this->search_list) > 0) ) { $this->domain = $this->search_list[0]; }
} else { throw new Net_DNS2_Exception( 'resolver file file provided is not readable: ' . $nameservers, Net_DNS2_Lookups::E_NS_INVALID_FILE ); } }
// // check the name servers // $this->checkServers();
return true; }
/** * checks the list of name servers to make sure they're set * * @param mixed $default a path to a resolv.conf file or an array of servers. * * @return boolean * @throws Net_DNS2_Exception * @access protected * */ protected function checkServers($default = null) { if (empty($this->nameservers)) {
if (isset($default)) {
$this->setServers($default); } else {
throw new Net_DNS2_Exception( 'empty name servers list; you must provide a list of name '. 'servers, or the path to a resolv.conf file.', Net_DNS2_Lookups::E_NS_INVALID_ENTRY ); } } return true; }
/** * adds a TSIG RR object for authentication * * @param string $keyname the key name to use for the TSIG RR * @param string $signature the key to sign the request. * * @return boolean * @access public * @since function available since release 1.1.0 * */ public function signTSIG($keyname, $signature = '') { // // if the TSIG was pre-created and passed in, then we can just used // it as provided. // if ($keyname instanceof Net_DNS2_RR_TSIG) {
$this->auth_signature = $keyname;
} else {
// // otherwise create the TSIG RR, but don't add it just yet; TSIG needs // to be added as the last additional entry- so we'll add it just // before we send. // $this->auth_signature = Net_DNS2_RR::fromString( strtolower(trim($keyname)) . ' TSIG '. $signature ); } return true; }
/** * adds a SIG RR object for authentication * * @param string $filename the name of a file to load the signature from. * * @return boolean * @throws Net_DNS2_Exception * @access public * @since function available since release 1.1.0 * */ public function signSIG0($filename) { // // check for OpenSSL // if (extension_loaded('openssl') === false) { throw new Net_DNS2_Exception( 'the OpenSSL extension is required to use SIG(0).', Net_DNS2_Lookups::E_OPENSSL_UNAVAIL ); }
// // if the SIG was pre-created, then use it as-is // if ($filename instanceof Net_DNS2_RR_SIG) {
$this->auth_signature = $filename;
} else { // // otherwise, it's filename which needs to be parsed and processed. // $private = new Net_DNS2_PrivateKey($filename);
// // create a new Net_DNS2_RR_SIG object // $this->auth_signature = new Net_DNS2_RR_SIG();
// // reset some values // $this->auth_signature->name = $private->signname; $this->auth_signature->ttl = 0; $this->auth_signature->class = 'ANY';
// // these values are pulled from the private key // $this->auth_signature->algorithm = $private->algorithm; $this->auth_signature->keytag = $private->keytag; $this->auth_signature->signname = $private->signname;
// // these values are hard-coded for SIG0 // $this->auth_signature->typecovered = 'SIG0'; $this->auth_signature->labels = 0; $this->auth_signature->origttl = 0;
// // generate the dates // $t = time();
$this->auth_signature->sigincep = gmdate('YmdHis', $t); $this->auth_signature->sigexp = gmdate('YmdHis', $t + 500);
// // store the private key in the SIG object for later. // $this->auth_signature->private_key = $private; }
// // only RSAMD5 and RSASHA1 are supported for SIG(0) // switch($this->auth_signature->algorithm) { case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5: case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1: break; default: throw new Net_DNS2_Exception( 'only asymmetric algorithms work with SIG(0)!', Net_DNS2_Lookups::E_OPENSSL_INV_ALGO ); }
return true; }
/** * returns true/false if the given address is a valid IPv4 address * * @param string $_address the IPv4 address to check * * @return boolean returns true/false if the address is IPv4 address * @access public * */ public static function isIPv4($_address) { // // use filter_var() if it's available; it's faster than preg // if (extension_loaded('filter') == true) {
if (filter_var( $_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) == false) { return false; } } else {
// // do the main check here; // if (inet_pton($_address) === false) { return false; }
// // then make sure we're not a IPv6 address // if (preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_address ) == 0) { return false; } }
return true; } /** * returns true/false if the given address is a valid IPv6 address * * @param string $_address the IPv6 address to check * * @return boolean returns true/false if the address is IPv6 address * @access public * */ public static function isIPv6($_address) { // // use filter_var() if it's available; it's faster than preg // if (extension_loaded('filter') == true) { if (filter_var( $_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) == false) { return false; } } else {
// // do the main check here // if (inet_pton($_address) === false) { return false; }
// // then make sure it doesn't match a IPv4 address // if (preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_address ) == 1) { return false; } }
return true; }
/** * formats the given IPv6 address as a fully expanded IPv6 address * * @param string $_address the IPv6 address to expand * * @return string the fully expanded IPv6 address * @access public * */ public static function expandIPv6($_address) { if (strpos($_address, '::') !== false) { $part = explode('::', $_address); $part[0] = explode(':', $part[0]); $part[1] = explode(':', $part[1]); $missing = array(); $x = (8 - (count($part[0]) + count($part[1]))); for ($i = 0; $i < $x; $i++) { array_push($missing, '0000'); }
$missing = array_merge($part[0], $missing); $part = array_merge($missing, $part[1]);
} else { $part = explode(':', $_address); }
foreach ($part as &$p) { while (strlen($p) < 4) { $p = '0' . $p; } }
unset($p); $result = implode(':', $part);
if (strlen($result) == 39) { return $result; } else { return false; } }
/** * sends a standard Net_DNS2_Packet_Request packet * * @param Net_DNS2_Packet $request a Net_DNS2_Packet_Request object * @param boolean $use_tcp true/false if the function should * use TCP for the request * * @return mixed returns a Net_DNS2_Packet_Response object, or false on error * @throws Net_DNS2_Exception * @access protected * */ protected function sendPacket(Net_DNS2_Packet $request, $use_tcp) { // // get the data from the packet // $data = $request->get(); if (strlen($data) < Net_DNS2_Lookups::DNS_HEADER_SIZE) {
throw new Net_DNS2_Exception( 'invalid or empty packet for sending!', Net_DNS2_Lookups::E_PACKET_INVALID ); }
reset($this->nameservers); // // randomize the name server list if it's asked for // if ($this->ns_random == true) {
shuffle($this->nameservers); }
// // loop so we can handle server errors // $response = null; $ns = ''; $tcp_fallback = false;
while (1) {
// // grab the next DNS server // if ($tcp_fallback == false) {
$ns = each($this->nameservers); if ($ns === false) {
throw new Net_DNS2_Exception( 'every name server provided has failed: ' . $this->_last_socket_error, Net_DNS2_Lookups::E_NS_FAILED ); }
$ns = $ns[1]; }
// // if the use TCP flag (force TCP) is set, or the packet is bigger // than 512 bytes, use TCP for sending the packet // if ( ($use_tcp == true) || (strlen($data) > Net_DNS2_Lookups::DNS_MAX_UDP_SIZE) || ($tcp_fallback == true) ) { $tcp_fallback = false;
// // create the socket object // if ( (!isset($this->sock['tcp'][$ns])) || (!($this->sock['tcp'][$ns] instanceof Net_DNS2_Socket)) ) { if ($this->sockets_enabled === true) {
$this->sock['tcp'][$ns] = new Net_DNS2_Socket_Sockets( SOCK_STREAM, $ns, $this->dns_port, $this->timeout ); } else {
$this->sock['tcp'][$ns] = new Net_DNS2_Socket_Streams( SOCK_STREAM, $ns, $this->dns_port, $this->timeout ); } }
// // if a local IP address / port is set, then add it // if (strlen($this->local_host) > 0) {
$this->sock['tcp'][$ns]->bindAddress( $this->local_host, $this->local_port ); }
// // open it; if it fails, continue in the while loop // if ($this->sock['tcp'][$ns]->open() === false) {
$this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; continue; }
// // write the data to the socket; if it fails, continue on // the while loop // if ($this->sock['tcp'][$ns]->write($data) === false) {
$this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; continue; }
// // read the content, using select to wait for a response // $size = 0;
$result = $this->sock['tcp'][$ns]->read($size); if ( ($result === false) || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE) ) { $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; continue; }
// // create the packet object // $response = new Net_DNS2_Packet_Response($result, $size); break;
} else {
// // create the socket object // if ( (!isset($this->sock['udp'][$ns])) || (!($this->sock['udp'][$ns] instanceof Net_DNS2_Socket)) ) { if ($this->sockets_enabled === true) {
$this->sock['udp'][$ns] = new Net_DNS2_Socket_Sockets( SOCK_DGRAM, $ns, $this->dns_port, $this->timeout ); } else {
$this->sock['udp'][$ns] = new Net_DNS2_Socket_Streams( SOCK_DGRAM, $ns, $this->dns_port, $this->timeout ); } }
// // if a local IP address / port is set, then add it // if (strlen($this->local_host) > 0) {
$this->sock['udp'][$ns]->bindAddress( $this->local_host, $this->local_port ); }
// // open it // if ($this->sock['udp'][$ns]->open() === false) {
$this->_last_socket_error = $this->sock['udp'][$ns]->last_error; continue; }
// // write the data to the socket // if ($this->sock['udp'][$ns]->write($data) === false) {
$this->_last_socket_error = $this->sock['udp'][$ns]->last_error; continue; }
// // read the content, using select to wait for a response // $size = 0;
$result = $this->sock['udp'][$ns]->read($size); if (( $result === false) || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE) ) { $this->_last_socket_error = $this->sock['udp'][$ns]->last_error; continue; }
// // create the packet object // $response = new Net_DNS2_Packet_Response($result, $size);
// // check the packet header for a trucated bit; if it was truncated, // then re-send the request as TCP. // if ($response->header->tc == 1) {
$tcp_fallback = true; continue; }
break; } }
if (is_null($response)) {
return false; }
// // make sure header id's match between the request and response // if ($request->header->id != $response->header->id) {
throw new Net_DNS2_Exception( 'invalid header: the request and response id do not match.', Net_DNS2_Lookups::E_HEADER_INVALID ); }
// // make sure the response is actually a response // // 0 = query, 1 = response // if ($response->header->qr != Net_DNS2_Lookups::QR_RESPONSE) {
throw new Net_DNS2_Exception( 'invalid header: the response provided is not a response packet.', Net_DNS2_Lookups::E_HEADER_INVALID ); }
// // make sure the response code in the header is ok // if ($response->header->rcode != Net_DNS2_Lookups::RCODE_NOERROR) {
throw new Net_DNS2_Exception( 'DNS request failed: ' . Net_DNS2_Lookups::$result_code_messages[$response->header->rcode], $response->header->rcode ); }
return $response; } }
/* * Local variables: * tab-width: 4 * c-basic-offset: 4 * c-hanging-comment-ender-p: nil * End: */ ?>
|