PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/includes/net/dns2.php

https://bitbucket.org/speedealing/speedealing
PHP | 1100 lines | 433 code | 175 blank | 492 comment | 124 complexity | fb9db7aa7f7aded37f13a487b676af00 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, GPL-3.0, MIT
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * DNS Library for handling lookups and updates.
  5. *
  6. * PHP Version 5
  7. *
  8. * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>.
  9. * All rights reserved.
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions
  13. * are met:
  14. *
  15. * * Redistributions of source code must retain the above copyright
  16. * notice, this list of conditions and the following disclaimer.
  17. *
  18. * * Redistributions in binary form must reproduce the above copyright
  19. * notice, this list of conditions and the following disclaimer in
  20. * the documentation and/or other materials provided with the
  21. * distribution.
  22. *
  23. * * Neither the name of Mike Pultz nor the names of his contributors
  24. * may be used to endorse or promote products derived from this
  25. * software without specific prior written permission.
  26. *
  27. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  28. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  29. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  30. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  31. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  32. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  33. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  34. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  35. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  37. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  38. * POSSIBILITY OF SUCH DAMAGE.
  39. *
  40. * @category Networking
  41. * @package Net_DNS2
  42. * @author Mike Pultz <mike@mikepultz.com>
  43. * @copyright 2010 Mike Pultz <mike@mikepultz.com>
  44. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  45. * @version SVN: $Id: DNS2.php 180 2012-11-30 00:46:05Z mike.pultz $
  46. * @link http://pear.php.net/package/Net_DNS2
  47. * @since File available since Release 0.6.0
  48. *
  49. */
  50. /*
  51. * register the auto-load function
  52. *
  53. */
  54. spl_autoload_register('Net_DNS2::autoload');
  55. /**
  56. * This is the base class for the Net_DNS2_Resolver and Net_DNS2_Updater
  57. * classes.
  58. *
  59. * @category Networking
  60. * @package Net_DNS2
  61. * @author Mike Pultz <mike@mikepultz.com>
  62. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  63. * @link http://pear.php.net/package/Net_DNS2
  64. * @see Net_DNS2_Resolver, Net_DNS2_Updater
  65. *
  66. */
  67. class Net_DNS2 {
  68. /*
  69. * the current version of this library
  70. */
  71. const VERSION = '1.2.5';
  72. /*
  73. * the default path to a resolv.conf file
  74. */
  75. const RESOLV_CONF = '/etc/resolv.conf';
  76. /*
  77. * use TCP only (true/false)
  78. */
  79. public $use_tcp = false;
  80. /*
  81. * DNS Port to use (53)
  82. */
  83. public $dns_port = 53;
  84. /*
  85. * the ip/port for use as a local socket
  86. */
  87. public $local_host = '';
  88. public $local_port = 0;
  89. /*
  90. * timeout value for socket connections
  91. */
  92. public $timeout = 5;
  93. /*
  94. * randomize the name servers list
  95. */
  96. public $ns_random = false;
  97. /*
  98. * default domains
  99. */
  100. public $domain = '';
  101. /*
  102. * domain search list - not actually used right now
  103. */
  104. public $search_list = array();
  105. /*
  106. * enable cache; either "shared", "file" or "none"
  107. */
  108. public $cache_type = 'none';
  109. /*
  110. * file name to use for shared memory segment or file cache
  111. */
  112. public $cache_file = '/tmp/net_dns2.cache';
  113. /*
  114. * the max size of the cache file (in bytes)
  115. */
  116. public $cache_size = 10000;
  117. /*
  118. * the method to use for storing cache data; either "serialize" or "json"
  119. *
  120. * json is faster, but can't remember the class names (everything comes back
  121. * as a "stdClass Object"; all the data is the same though. serialize is
  122. * slower, but will have all the class info.
  123. *
  124. * defaults to 'serialize'
  125. */
  126. public $cache_serializer = 'serialize';
  127. /*
  128. * by default, according to RFC 1034
  129. *
  130. * CNAME RRs cause special action in DNS software. When a name server
  131. * fails to find a desired RR in the resource set associated with the
  132. * domain name, it checks to see if the resource set consists of a CNAME
  133. * record with a matching class. If so, the name server includes the CNAME
  134. * record in the response and restarts the query at the domain name
  135. * specified in the data field of the CNAME record.
  136. *
  137. * this can cause "unexpected" behavious, since i'm sure *most* people
  138. * don't know DNS does this; there may be cases where Net_DNS2 returns a
  139. * positive response, even though the hostname the user looked up did not
  140. * actually exist.
  141. *
  142. * strict_query_mode means that if the hostname that was looked up isn't
  143. * actually in the answer section of the response, Net_DNS2 will return an
  144. * empty answer section, instead of an answer section that could contain
  145. * CNAME records.
  146. *
  147. */
  148. public $strict_query_mode = false;
  149. /*
  150. * if we should set the recursion desired bit to 1 or 0.
  151. *
  152. * by default this is set to true, we want the DNS server to perform a recursive
  153. * request. If set to false, the RD bit will be set to 0, and the server will not
  154. * perform recursion on the request.
  155. */
  156. public $recurse = true;
  157. /*
  158. * local sockets
  159. */
  160. protected $sock = array('udp' => array(), 'tcp' => array());
  161. /*
  162. * name server list
  163. */
  164. protected $nameservers = array();
  165. /*
  166. * if the socket extension is loaded
  167. */
  168. protected $sockets_enabled = false;
  169. /*
  170. * the TSIG or SIG RR object for authentication
  171. */
  172. protected $auth_signature = null;
  173. /*
  174. * the shared memory segment id for the local cache
  175. */
  176. protected $cache = null;
  177. /*
  178. * internal setting for enabling cache
  179. */
  180. protected $use_cache = false;
  181. /*
  182. * the last erro message returned by the sockets class
  183. */
  184. private $_last_socket_error = '';
  185. /**
  186. * Constructor - base constructor for the Resolver and Updater
  187. *
  188. * @param mixed $options array of options or null for none
  189. *
  190. * @throws Net_DNS2_Exception
  191. * @access public
  192. *
  193. */
  194. public function __construct(array $options = null) {
  195. //
  196. // check for the sockets extension
  197. //
  198. $this->sockets_enabled = extension_loaded('sockets');
  199. //
  200. // load any options that were provided
  201. //
  202. if (!empty($options)) {
  203. foreach ($options as $key => $value) {
  204. if ($key == 'nameservers') {
  205. $this->setServers($value);
  206. } else {
  207. $this->$key = $value;
  208. }
  209. }
  210. }
  211. //
  212. // if we're set to use the local shared memory cache, then
  213. // make sure it's been initialized
  214. //
  215. switch ($this->cache_type) {
  216. case 'shared':
  217. if (extension_loaded('shmop')) {
  218. $this->cache = new Net_DNS2_Cache_Shm;
  219. $this->use_cache = true;
  220. } else {
  221. throw new Net_DNS2_Exception(
  222. 'shmop library is not available for cache', Net_DNS2_Lookups::E_CACHE_SHM_UNAVAIL
  223. );
  224. }
  225. break;
  226. case 'file':
  227. $this->cache = new Net_DNS2_Cache_File;
  228. $this->use_cache = true;
  229. break;
  230. case 'none':
  231. $this->use_cache = false;
  232. break;
  233. default:
  234. throw new Net_DNS2_Exception(
  235. 'un-supported cache type: ' . $this->cache_type, Net_DNS2_Lookups::E_CACHE_UNSUPPORTED
  236. );
  237. }
  238. }
  239. /**
  240. * autoload call-back function; used to auto-load classes
  241. *
  242. * @param string $name the name of the class
  243. *
  244. * @return void
  245. * @access public
  246. *
  247. */
  248. static public function autoload($name) {
  249. //
  250. // only auto-load our classes
  251. //
  252. if (strncmp($name, 'Net_DNS2', 8) == 0) {
  253. include_once DOL_DOCUMENT_ROOT . '/includes/' . str_replace('_', '/', strtolower($name)) . '.php';
  254. }
  255. return;
  256. }
  257. /**
  258. * sets the name servers to be used
  259. *
  260. * @param mixed $nameservers either an array of name servers, or a file name
  261. * to parse, assuming it's in the resolv.conf format
  262. *
  263. * @return boolean
  264. * @throws Net_DNS2_Exception
  265. * @access public
  266. *
  267. */
  268. public function setServers($nameservers) {
  269. //
  270. // if it's an array, then use it directly
  271. //
  272. // otherwise, see if it's a path to a resolv.conf file and if so, load it
  273. //
  274. if (is_array($nameservers)) {
  275. $this->nameservers = $nameservers;
  276. } else {
  277. //
  278. // check to see if the file is readable
  279. //
  280. if (is_readable($nameservers) === true) {
  281. $data = file_get_contents($nameservers);
  282. if ($data === false) {
  283. throw new Net_DNS2_Exception(
  284. 'failed to read contents of file: ' . $nameservers, Net_DNS2_Lookups::E_NS_INVALID_FILE
  285. );
  286. }
  287. $lines = explode("\n", $data);
  288. foreach ($lines as $line) {
  289. $line = trim($line);
  290. //
  291. // ignore empty lines, and lines that are commented out
  292. //
  293. if ((strlen($line) == 0) || ($line[0] == '#') || ($line[0] == ';')
  294. ) {
  295. continue;
  296. }
  297. list($key, $value) = preg_split('/\s+/', $line, 2);
  298. $key = trim(strtolower($key));
  299. $value = trim(strtolower($value));
  300. switch ($key) {
  301. case 'nameserver':
  302. //
  303. // nameserver can be a IPv4 or IPv6 address
  304. //
  305. if ((self::isIPv4($value) == true) || (self::isIPv6($value) == true)
  306. ) {
  307. $this->nameservers[] = $value;
  308. } else {
  309. throw new Net_DNS2_Exception(
  310. 'invalid nameserver entry: ' . $value, Net_DNS2_Lookups::E_NS_INVALID_ENTRY
  311. );
  312. }
  313. break;
  314. case 'domain':
  315. $this->domain = $value;
  316. break;
  317. case 'search':
  318. $this->search_list = preg_split('/\s+/', $value);
  319. break;
  320. default:
  321. ;
  322. }
  323. }
  324. //
  325. // if we don't have a domain, but we have a search list, then
  326. // take the first entry on the search list as the domain
  327. //
  328. if ((strlen($this->domain) == 0) && (count($this->search_list) > 0)
  329. ) {
  330. $this->domain = $this->search_list[0];
  331. }
  332. } else {
  333. throw new Net_DNS2_Exception(
  334. 'resolver file file provided is not readable: ' . $nameservers, Net_DNS2_Lookups::E_NS_INVALID_FILE
  335. );
  336. }
  337. }
  338. //
  339. // check the name servers
  340. //
  341. $this->checkServers();
  342. return true;
  343. }
  344. /**
  345. * checks the list of name servers to make sure they're set
  346. *
  347. * @param mixed $default a path to a resolv.conf file or an array of servers.
  348. *
  349. * @return boolean
  350. * @throws Net_DNS2_Exception
  351. * @access protected
  352. *
  353. */
  354. protected function checkServers($default = null) {
  355. if (empty($this->nameservers)) {
  356. if (isset($default)) {
  357. $this->setServers($default);
  358. } else {
  359. throw new Net_DNS2_Exception(
  360. 'empty name servers list; you must provide a list of name ' .
  361. 'servers, or the path to a resolv.conf file.', Net_DNS2_Lookups::E_NS_INVALID_ENTRY
  362. );
  363. }
  364. }
  365. return true;
  366. }
  367. /**
  368. * adds a TSIG RR object for authentication
  369. *
  370. * @param string $keyname the key name to use for the TSIG RR
  371. * @param string $signature the key to sign the request.
  372. *
  373. * @return boolean
  374. * @access public
  375. * @since function available since release 1.1.0
  376. *
  377. */
  378. public function signTSIG($keyname, $signature = '') {
  379. //
  380. // if the TSIG was pre-created and passed in, then we can just used
  381. // it as provided.
  382. //
  383. if ($keyname instanceof Net_DNS2_RR_TSIG) {
  384. $this->auth_signature = $keyname;
  385. } else {
  386. //
  387. // otherwise create the TSIG RR, but don't add it just yet; TSIG needs
  388. // to be added as the last additional entry- so we'll add it just
  389. // before we send.
  390. //
  391. $this->auth_signature = Net_DNS2_RR::fromString(
  392. strtolower(trim($keyname)) .
  393. ' TSIG ' . $signature
  394. );
  395. }
  396. return true;
  397. }
  398. /**
  399. * adds a SIG RR object for authentication
  400. *
  401. * @param string $filename the name of a file to load the signature from.
  402. *
  403. * @return boolean
  404. * @throws Net_DNS2_Exception
  405. * @access public
  406. * @since function available since release 1.1.0
  407. *
  408. */
  409. public function signSIG0($filename) {
  410. //
  411. // check for OpenSSL
  412. //
  413. if (extension_loaded('openssl') === false) {
  414. throw new Net_DNS2_Exception(
  415. 'the OpenSSL extension is required to use SIG(0).', Net_DNS2_Lookups::E_OPENSSL_UNAVAIL
  416. );
  417. }
  418. //
  419. // if the SIG was pre-created, then use it as-is
  420. //
  421. if ($filename instanceof Net_DNS2_RR_SIG) {
  422. $this->auth_signature = $filename;
  423. } else {
  424. //
  425. // otherwise, it's filename which needs to be parsed and processed.
  426. //
  427. $private = new Net_DNS2_PrivateKey($filename);
  428. //
  429. // create a new Net_DNS2_RR_SIG object
  430. //
  431. $this->auth_signature = new Net_DNS2_RR_SIG();
  432. //
  433. // reset some values
  434. //
  435. $this->auth_signature->name = $private->signname;
  436. $this->auth_signature->ttl = 0;
  437. $this->auth_signature->class = 'ANY';
  438. //
  439. // these values are pulled from the private key
  440. //
  441. $this->auth_signature->algorithm = $private->algorithm;
  442. $this->auth_signature->keytag = $private->keytag;
  443. $this->auth_signature->signname = $private->signname;
  444. //
  445. // these values are hard-coded for SIG0
  446. //
  447. $this->auth_signature->typecovered = 'SIG0';
  448. $this->auth_signature->labels = 0;
  449. $this->auth_signature->origttl = 0;
  450. //
  451. // generate the dates
  452. //
  453. $t = time();
  454. $this->auth_signature->sigincep = gmdate('YmdHis', $t);
  455. $this->auth_signature->sigexp = gmdate('YmdHis', $t + 500);
  456. //
  457. // store the private key in the SIG object for later.
  458. //
  459. $this->auth_signature->private_key = $private;
  460. }
  461. //
  462. // only RSAMD5 and RSASHA1 are supported for SIG(0)
  463. //
  464. switch ($this->auth_signature->algorithm) {
  465. case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5:
  466. case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1:
  467. break;
  468. default:
  469. throw new Net_DNS2_Exception(
  470. 'only asymmetric algorithms work with SIG(0)!', Net_DNS2_Lookups::E_OPENSSL_INV_ALGO
  471. );
  472. }
  473. return true;
  474. }
  475. /**
  476. * PHP doesn't support unsigned integers, but many of the RR's return
  477. * unsigned values (like SOA), so there is the possibility that the
  478. * value will overrun on 32bit systems, and you'll end up with a
  479. * negative value.
  480. *
  481. * 64bit systems are not affected, as their PHP_IN_MAX value should
  482. * be 64bit (ie 9223372036854775807)
  483. *
  484. * This function returns a negative integer value, as a string, with
  485. * the correct unsigned value.
  486. *
  487. * @param string $_int the unsigned integer value to check
  488. *
  489. * @return string returns the unsigned value as a string.
  490. * @access public
  491. *
  492. */
  493. public static function expandUint32($_int) {
  494. if (($_int < 0) && (PHP_INT_MAX == 2147483647)) {
  495. return sprintf('%u', $_int);
  496. } else {
  497. return $_int;
  498. }
  499. }
  500. /**
  501. * returns true/false if the given address is a valid IPv4 address
  502. *
  503. * @param string $_address the IPv4 address to check
  504. *
  505. * @return boolean returns true/false if the address is IPv4 address
  506. * @access public
  507. *
  508. */
  509. public static function isIPv4($_address) {
  510. //
  511. // use filter_var() if it's available; it's faster than preg
  512. //
  513. if (extension_loaded('filter') == true) {
  514. if (filter_var(
  515. $_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4
  516. ) == false) {
  517. return false;
  518. }
  519. } else {
  520. //
  521. // do the main check here;
  522. //
  523. if (inet_pton($_address) === false) {
  524. return false;
  525. }
  526. //
  527. // then make sure we're not a IPv6 address
  528. //
  529. if (preg_match(
  530. '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_address
  531. ) == 0) {
  532. return false;
  533. }
  534. }
  535. return true;
  536. }
  537. /**
  538. * returns true/false if the given address is a valid IPv6 address
  539. *
  540. * @param string $_address the IPv6 address to check
  541. *
  542. * @return boolean returns true/false if the address is IPv6 address
  543. * @access public
  544. *
  545. */
  546. public static function isIPv6($_address) {
  547. //
  548. // use filter_var() if it's available; it's faster than preg
  549. //
  550. if (extension_loaded('filter') == true) {
  551. if (filter_var(
  552. $_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6
  553. ) == false) {
  554. return false;
  555. }
  556. } else {
  557. //
  558. // do the main check here
  559. //
  560. if (inet_pton($_address) === false) {
  561. return false;
  562. }
  563. //
  564. // then make sure it doesn't match a IPv4 address
  565. //
  566. if (preg_match(
  567. '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_address
  568. ) == 1) {
  569. return false;
  570. }
  571. }
  572. return true;
  573. }
  574. /**
  575. * formats the given IPv6 address as a fully expanded IPv6 address
  576. *
  577. * @param string $_address the IPv6 address to expand
  578. *
  579. * @return string the fully expanded IPv6 address
  580. * @access public
  581. *
  582. */
  583. public static function expandIPv6($_address) {
  584. if (strpos($_address, '::') !== false) {
  585. $part = explode('::', $_address);
  586. $part[0] = explode(':', $part[0]);
  587. $part[1] = explode(':', $part[1]);
  588. $missing = array();
  589. $x = (8 - (count($part[0]) + count($part[1])));
  590. for ($i = 0; $i < $x; $i++) {
  591. array_push($missing, '0000');
  592. }
  593. $missing = array_merge($part[0], $missing);
  594. $part = array_merge($missing, $part[1]);
  595. } else {
  596. $part = explode(':', $_address);
  597. }
  598. foreach ($part as &$p) {
  599. while (strlen($p) < 4) {
  600. $p = '0' . $p;
  601. }
  602. }
  603. unset($p);
  604. $result = implode(':', $part);
  605. if (strlen($result) == 39) {
  606. return $result;
  607. } else {
  608. return false;
  609. }
  610. }
  611. /**
  612. * sends a standard Net_DNS2_Packet_Request packet
  613. *
  614. * @param Net_DNS2_Packet $request a Net_DNS2_Packet_Request object
  615. * @param boolean $use_tcp true/false if the function should
  616. * use TCP for the request
  617. *
  618. * @return mixed returns a Net_DNS2_Packet_Response object, or false on error
  619. * @throws Net_DNS2_Exception
  620. * @access protected
  621. *
  622. */
  623. protected function sendPacket(Net_DNS2_Packet $request, $use_tcp) {
  624. //
  625. // get the data from the packet
  626. //
  627. $data = $request->get();
  628. if (strlen($data) < Net_DNS2_Lookups::DNS_HEADER_SIZE) {
  629. throw new Net_DNS2_Exception(
  630. 'invalid or empty packet for sending!', Net_DNS2_Lookups::E_PACKET_INVALID
  631. );
  632. }
  633. reset($this->nameservers);
  634. //
  635. // randomize the name server list if it's asked for
  636. //
  637. if ($this->ns_random == true) {
  638. shuffle($this->nameservers);
  639. }
  640. //
  641. // loop so we can handle server errors
  642. //
  643. $response = null;
  644. $ns = '';
  645. $socket_type = null;
  646. $tcp_fallback = false;
  647. while (1) {
  648. //
  649. // grab the next DNS server
  650. //
  651. if ($tcp_fallback == false) {
  652. $ns = each($this->nameservers);
  653. if ($ns === false) {
  654. throw new Net_DNS2_Exception(
  655. 'every name server provided has failed: ' .
  656. $this->_last_socket_error, Net_DNS2_Lookups::E_NS_FAILED
  657. );
  658. }
  659. $ns = $ns[1];
  660. }
  661. //
  662. // if the use TCP flag (force TCP) is set, or the packet is bigger
  663. // than 512 bytes, use TCP for sending the packet
  664. //
  665. if (($use_tcp == true) || (strlen($data) > Net_DNS2_Lookups::DNS_MAX_UDP_SIZE) || ($tcp_fallback == true)
  666. ) {
  667. $tcp_fallback = false;
  668. $socket_type = Net_DNS2_Socket::SOCK_STREAM;
  669. //
  670. // create the socket object
  671. //
  672. if ((!isset($this->sock['tcp'][$ns])) || (!($this->sock['tcp'][$ns] instanceof Net_DNS2_Socket))
  673. ) {
  674. if ($this->sockets_enabled === true) {
  675. $this->sock['tcp'][$ns] = new Net_DNS2_Socket_Sockets(
  676. Net_DNS2_Socket::SOCK_STREAM, $ns, $this->dns_port, $this->timeout
  677. );
  678. } else {
  679. $this->sock['tcp'][$ns] = new Net_DNS2_Socket_Streams(
  680. Net_DNS2_Socket::SOCK_STREAM, $ns, $this->dns_port, $this->timeout
  681. );
  682. }
  683. }
  684. //
  685. // if a local IP address / port is set, then add it
  686. //
  687. if (strlen($this->local_host) > 0) {
  688. $this->sock['tcp'][$ns]->bindAddress(
  689. $this->local_host, $this->local_port
  690. );
  691. }
  692. //
  693. // open it; if it fails, continue in the while loop
  694. //
  695. if ($this->sock['tcp'][$ns]->open() === false) {
  696. $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error;
  697. continue;
  698. }
  699. //
  700. // write the data to the socket; if it fails, continue on
  701. // the while loop
  702. //
  703. if ($this->sock['tcp'][$ns]->write($data) === false) {
  704. $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error;
  705. continue;
  706. }
  707. //
  708. // read the content, using select to wait for a response
  709. //
  710. $size = 0;
  711. $result = null;
  712. //
  713. // handle zone transfer requests differently than other requests.
  714. //
  715. if ($request->question[0]->qtype == 'AXFR') {
  716. $soa_count = 0;
  717. while (1) {
  718. //
  719. // read the data off the socket
  720. //
  721. $result = $this->sock['tcp'][$ns]->read($size);
  722. if (($result === false) || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE)
  723. ) {
  724. $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error;
  725. break;
  726. }
  727. //
  728. // parse the first chunk as a packet
  729. //
  730. $chunk = new Net_DNS2_Packet_Response($result, $size);
  731. //
  732. // if this is the first packet, then clone it directly, then
  733. // go through it to see if there are two SOA records
  734. // (indicating that it's the only packet)
  735. //
  736. if (is_null($response) == true) {
  737. $response = clone $chunk;
  738. //
  739. // look for a failed response; if the zone transfer
  740. // failed, then we don't need to do anything else at this
  741. // point, and we should just break out.
  742. //
  743. if ($response->header->rcode != Net_DNS2_Lookups::RCODE_NOERROR) {
  744. break;
  745. }
  746. //
  747. // go through each answer
  748. //
  749. foreach ($response->answer as $index => $rr) {
  750. //
  751. // count the SOA records
  752. //
  753. if ($rr->type == 'SOA') {
  754. $soa_count++;
  755. }
  756. }
  757. //
  758. // if we have 2 or more SOA records, then we're done;
  759. // otherwise continue out so we read the rest of the
  760. // packets off the socket
  761. //
  762. if ($soa_count >= 2) {
  763. break;
  764. } else {
  765. continue;
  766. }
  767. } else {
  768. //
  769. // go through all these answers, and look for SOA records
  770. //
  771. foreach ($chunk->answer as $index => $rr) {
  772. //
  773. // count the number of SOA records we find
  774. //
  775. if ($rr->type == 'SOA') {
  776. $soa_count++;
  777. }
  778. //
  779. // add the records to a single response object
  780. //
  781. $response->answer[] = $rr;
  782. }
  783. //
  784. // if we've found the second SOA record, we're done
  785. //
  786. if ($soa_count >= 2) {
  787. break;
  788. }
  789. }
  790. }
  791. } else {
  792. $result = $this->sock['tcp'][$ns]->read($size);
  793. if (($result === false) || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE)
  794. ) {
  795. $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error;
  796. continue;
  797. }
  798. //
  799. // create the packet object
  800. //
  801. $response = new Net_DNS2_Packet_Response($result, $size);
  802. }
  803. break;
  804. } else {
  805. $socket_type = Net_DNS2_Socket::SOCK_DGRAM;
  806. //
  807. // create the socket object
  808. //
  809. if ((!isset($this->sock['udp'][$ns])) || (!($this->sock['udp'][$ns] instanceof Net_DNS2_Socket))
  810. ) {
  811. if ($this->sockets_enabled === true) {
  812. $this->sock['udp'][$ns] = new Net_DNS2_Socket_Sockets(
  813. Net_DNS2_Socket::SOCK_DGRAM, $ns, $this->dns_port, $this->timeout
  814. );
  815. } else {
  816. $this->sock['udp'][$ns] = new Net_DNS2_Socket_Streams(
  817. Net_DNS2_Socket::SOCK_DGRAM, $ns, $this->dns_port, $this->timeout
  818. );
  819. }
  820. }
  821. //
  822. // if a local IP address / port is set, then add it
  823. //
  824. if (strlen($this->local_host) > 0) {
  825. $this->sock['udp'][$ns]->bindAddress(
  826. $this->local_host, $this->local_port
  827. );
  828. }
  829. //
  830. // open it
  831. //
  832. if ($this->sock['udp'][$ns]->open() === false) {
  833. $this->_last_socket_error = $this->sock['udp'][$ns]->last_error;
  834. continue;
  835. }
  836. //
  837. // write the data to the socket
  838. //
  839. if ($this->sock['udp'][$ns]->write($data) === false) {
  840. $this->_last_socket_error = $this->sock['udp'][$ns]->last_error;
  841. continue;
  842. }
  843. //
  844. // read the content, using select to wait for a response
  845. //
  846. $size = 0;
  847. $result = $this->sock['udp'][$ns]->read($size);
  848. if (( $result === false) || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE)
  849. ) {
  850. $this->_last_socket_error = $this->sock['udp'][$ns]->last_error;
  851. continue;
  852. }
  853. //
  854. // create the packet object
  855. //
  856. $response = new Net_DNS2_Packet_Response($result, $size);
  857. if (is_null($response)) {
  858. throw new Net_DNS2_Exception(
  859. 'empty response object', Net_DNS2_Lookups::E_NS_FAILED
  860. );
  861. }
  862. //
  863. // check the packet header for a trucated bit; if it was truncated,
  864. // then re-send the request as TCP.
  865. //
  866. if ($response->header->tc == 1) {
  867. $tcp_fallback = true;
  868. continue;
  869. }
  870. break;
  871. }
  872. }
  873. //
  874. // if $response is null, then we didn't even try once; which shouldn't
  875. // actually ever happen
  876. //
  877. if (is_null($response)) {
  878. throw new Net_DNS2_Exception(
  879. 'empty response object', Net_DNS2_Lookups::E_NS_FAILED
  880. );
  881. }
  882. //
  883. // add the name server that the response came from to the response object,
  884. // and the socket type that was used.
  885. //
  886. $response->answer_from = $ns;
  887. $response->answer_socket_type = $socket_type;
  888. //
  889. // make sure header id's match between the request and response
  890. //
  891. if ($request->header->id != $response->header->id) {
  892. throw new Net_DNS2_Exception(
  893. 'invalid header: the request and response id do not match.', Net_DNS2_Lookups::E_HEADER_INVALID
  894. );
  895. }
  896. //
  897. // make sure the response is actually a response
  898. //
  899. // 0 = query, 1 = response
  900. //
  901. if ($response->header->qr != Net_DNS2_Lookups::QR_RESPONSE) {
  902. throw new Net_DNS2_Exception(
  903. 'invalid header: the response provided is not a response packet.', Net_DNS2_Lookups::E_HEADER_INVALID
  904. );
  905. }
  906. //
  907. // make sure the response code in the header is ok
  908. //
  909. if ($response->header->rcode != Net_DNS2_Lookups::RCODE_NOERROR) {
  910. throw new Net_DNS2_Exception(
  911. 'DNS request failed: ' .
  912. Net_DNS2_Lookups::$result_code_messages[$response->header->rcode], $response->header->rcode
  913. );
  914. }
  915. return $response;
  916. }
  917. }
  918. /*
  919. * Local variables:
  920. * tab-width: 4
  921. * c-basic-offset: 4
  922. * c-hanging-comment-ender-p: nil
  923. * End:
  924. */
  925. ?>