Resolver.php 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  1. <?php
  2. /*
  3. * License Information:
  4. *
  5. * Net_DNS: A resolver library for PHP
  6. * Copyright (c) 2002-2003 Eric Kilfoil eric@ypass.net
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. /* Net_DNS_Resolver object definition {{{ */
  23. /**
  24. * A DNS Resolver library
  25. *
  26. * Resolver library. Builds a DNS query packet, sends the packet to the
  27. * server and parses the reponse.
  28. *
  29. * @package Net_DNS
  30. */
  31. class Net_DNS_Resolver
  32. {
  33. /* class variable definitions {{{ */
  34. /**
  35. * An array of all nameservers to query
  36. *
  37. * An array of all nameservers to query
  38. *
  39. * @var array $nameservers
  40. * @access public
  41. */
  42. var $nameservers;
  43. /**
  44. * The UDP port to use for the query (default = 53)
  45. *
  46. * The UDP port to use for the query (default = 53)
  47. *
  48. * @var integer $port
  49. * @access public
  50. */
  51. var $port;
  52. /**
  53. * The domain in which the resolver client host resides.
  54. *
  55. * The domain in which the resolver client host resides.
  56. *
  57. * @var string $domain
  58. * @access public
  59. */
  60. var $domain;
  61. /**
  62. * The searchlist to apply to unqualified hosts
  63. *
  64. * An array of strings containg domains to apply to unqualified hosts
  65. * passed to the resolver.
  66. *
  67. * @var array $searchlist
  68. * @access public
  69. */
  70. var $searchlist;
  71. /**
  72. * The number of seconds between retransmission of unaswered queries
  73. *
  74. * The number of seconds between retransmission of unaswered queries
  75. *
  76. * @var integer $retrans
  77. * @access public
  78. */
  79. var $retrans;
  80. /**
  81. * The number of times unanswered requests should be retried
  82. *
  83. * The number of times unanswered requests should be retried
  84. *
  85. * @var integer $retry
  86. * @access public
  87. */
  88. var $retry;
  89. /**
  90. * Whether or not to use TCP (Virtual Circuits) instead of UDP
  91. *
  92. * If set to 0, UDP will be used unless TCP is required. TCP is
  93. * required for questions or responses greater than 512 bytes.
  94. *
  95. * @var boolean $usevc
  96. * @access public
  97. */
  98. var $usevc;
  99. /**
  100. * Unknown
  101. */
  102. var $stayopen;
  103. /**
  104. * Ignore TC (truncated) bit
  105. *
  106. * If the server responds with the TC bit set on a response, and $igntc
  107. * is set to 0, the resolver will automatically retransmit the request
  108. * using virtual circuits (TCP).
  109. *
  110. * @access public
  111. * @var boolean $igntc
  112. */
  113. var $igntc;
  114. /**
  115. * Recursion Desired
  116. *
  117. * Sets the value of the RD (recursion desired) bit in the header. If
  118. * the RD bit is set to 0, the server will not perform recursion on the
  119. * request.
  120. *
  121. * @var boolean $recurse
  122. * @access public
  123. */
  124. var $recurse;
  125. /**
  126. * Unknown
  127. */
  128. var $defnames;
  129. /**
  130. * Unknown
  131. */
  132. var $dnsrch;
  133. /**
  134. * Contains the value of the last error returned by the resolver.
  135. *
  136. * Contains the value of the last error returned by the resolver.
  137. *
  138. * @var string $errorstring
  139. * @access public
  140. */
  141. var $errorstring;
  142. /**
  143. * The origin of the packet.
  144. *
  145. * This contains a string containing the IP address of the name server
  146. * from which the answer was given.
  147. *
  148. * @var string $answerfrom
  149. * @access public
  150. */
  151. var $answerfrom;
  152. /**
  153. * The size of the answer packet.
  154. *
  155. * This contains a integer containing the size of the DNS packet the
  156. * server responded with.
  157. *
  158. * @var string $answersize
  159. * @access public
  160. */
  161. var $answersize;
  162. /**
  163. * The number of seconds after which a TCP connetion should timeout
  164. *
  165. * @var integer $tcp_timeout
  166. * @access public
  167. */
  168. var $tcp_timeout;
  169. /**
  170. * The location of the system resolv.conf file.
  171. *
  172. * The location of the system resolv.conf file.
  173. *
  174. * @var string $resolv_conf
  175. */
  176. var $resolv_conf = '/etc/resolv.conf';
  177. /**
  178. * The name of the user defined resolv.conf
  179. *
  180. * The resolver will attempt to look in both the current directory as
  181. * well as the user's home directory for a user defined resolver
  182. * configuration file
  183. *
  184. * @var string $dotfile
  185. * @see Net_DNS_Resolver::$confpath
  186. */
  187. var $dotfile = '.resolv.conf';
  188. /**
  189. * A array of directories to search for the user's resolver config
  190. *
  191. * A array of directories to search for the user's resolver config
  192. *
  193. * @var string $confpath
  194. * @see Net_DNS_Resolver::$dotfile
  195. */
  196. var $confpath;
  197. /**
  198. * debugging flag
  199. *
  200. * If set to TRUE (non-zero), debugging code will be displayed as the
  201. * resolver makes the request.
  202. *
  203. * @var boolean $debug;
  204. * @access public
  205. */
  206. var $debug;
  207. /**
  208. * use the (currently) experimental PHP socket library
  209. *
  210. * If set to TRUE (non-zero), the Resolver will attempt to use the
  211. * much more effecient PHP sockets extension (if available).
  212. *
  213. * @var boolean $useEnhancedSockets;
  214. * @access public
  215. */
  216. var $useEnhancedSockets = 1;
  217. /**
  218. * An array of sockets connected to a name servers
  219. *
  220. * @var array $sockets
  221. * @access private
  222. */
  223. var $sockets;
  224. /**
  225. * axfr tcp socket
  226. *
  227. * Used to store a PHP socket resource for a connection to a server
  228. *
  229. * @var resource $_axfr_sock;
  230. * @access private
  231. */
  232. var $_axfr_sock;
  233. /**
  234. * axfr resource record lsit
  235. *
  236. * Used to store a resource record list from a zone transfer
  237. *
  238. * @var resource $_axfr_rr;
  239. * @access private
  240. */
  241. var $_axfr_rr;
  242. /**
  243. * axfr soa count
  244. *
  245. * Used to store the number of soa records received from a zone transfer
  246. *
  247. * @var resource $_axfr_soa_count;
  248. * @access private
  249. */
  250. var $_axfr_soa_count;
  251. /* }}} */
  252. /* class constructor - Net_DNS_Resolver() {{{ */
  253. /**
  254. * Initializes the Resolver Object
  255. */
  256. function Net_DNS_Resolver()
  257. {
  258. $default = array(
  259. 'nameservers' => array(),
  260. 'port' => '53',
  261. 'domain' => '',
  262. 'searchlist' => array(),
  263. 'retrans' => 5,
  264. 'retry' => 4,
  265. 'usevc' => 0,
  266. 'stayopen' => 0,
  267. 'igntc' => 0,
  268. 'recurse' => 1,
  269. 'defnames' => 1,
  270. 'dnsrch' => 1,
  271. 'debug' => 0,
  272. 'errorstring' => 'unknown error or no error',
  273. 'answerfrom' => '',
  274. 'answersize' => 0,
  275. 'tcp_timeout' => 120
  276. );
  277. foreach ($default as $k => $v) {
  278. $this->{$k} = $v;
  279. }
  280. $this->confpath[0] = getenv('HOME');
  281. $this->confpath[1] = '.';
  282. $this->res_init();
  283. }
  284. /* }}} */
  285. /* Net_DNS_Resolver::res_init() {{{ */
  286. /**
  287. * Initalizes the resolver library
  288. *
  289. * res_init() searches for resolver library configuration files an
  290. * initializes the various properties of the resolver object.
  291. *
  292. * @see Net_DNS_Resolver::$resolv_conf, Net_DNS_Resolver::$dotfile,
  293. * Net_DNS_Resolver::$confpath, Net_DNS_Resolver::$searchlist,
  294. * Net_DNS_Resolver::$domain, Net_DNS_Resolver::$nameservers
  295. * @access public
  296. */
  297. function res_init()
  298. {
  299. $err = error_reporting(0);
  300. if (file_exists($this->resolv_conf) && is_readable($this->resolv_conf)) {
  301. $this->read_config($this->resolv_conf);
  302. }
  303. foreach ($this->confpath as $dir) {
  304. $file = "$dir/" . $this->dotfile;
  305. if (file_exists($file) && is_readable($file)) {
  306. $this->read_config($file);
  307. }
  308. }
  309. $this->read_env();
  310. if (!strlen($this->domain) && strlen($this->searchlist)) {
  311. $this->default{'domain'} = $this->default{'searchlist'}[0];
  312. } else if (! strlen($this->searchlist) && strlen($this->domain)) {
  313. $this->searchlist = array($this->domain);
  314. }
  315. error_reporting($err);
  316. }
  317. /* }}} */
  318. /* Net_DNS_Resolver::read_config {{{ */
  319. /**
  320. * Reads and parses a resolver configuration file
  321. *
  322. * @param string $file The name of the file to open and parse
  323. */
  324. function read_config($file)
  325. {
  326. if (! ($f = fopen($file, 'r'))) {
  327. $this->error = "can't open $file";
  328. }
  329. while (! feof($f)) {
  330. $line = chop(fgets($f, 10240));
  331. $line = ereg_replace('(.*)[;#].*', '\\1', $line);
  332. if (ereg("^[ \t]*$", $line, $regs)) {
  333. continue;
  334. }
  335. ereg("^[ \t]*([^ \t]+)[ \t]+([^ \t]+)", $line, $regs);
  336. $option = $regs[1];
  337. $value = $regs[2];
  338. switch ($option) {
  339. case 'domain':
  340. $this->domain = $regs[2];
  341. break;
  342. case 'search':
  343. $this->searchlist[count($this->searchlist)] = $regs[2];
  344. break;
  345. case 'nameserver':
  346. foreach (split(' ', $regs[2]) as $ns)
  347. $this->nameservers[count($this->nameservers)] = $ns;
  348. break;
  349. }
  350. }
  351. fclose($f);
  352. }
  353. /* }}} */
  354. /* Net_DNS_Resolver::read_env() {{{ */
  355. /**
  356. * Examines the environment for resolver config information
  357. */
  358. function read_env()
  359. {
  360. if (getenv('RES_NAMESERVERS')) {
  361. $this->nameservers = split(' ', getenv('RES_NAMESERVERS'));
  362. }
  363. if (getenv('RES_SEARCHLIST')) {
  364. $this->searchlist = split(' ', getenv('RES_SEARCHLIST'));
  365. }
  366. if (getenv('LOCALDOMAIN')) {
  367. $this->domain = getenv('LOCALDOMAIN');
  368. }
  369. if (getenv('RES_OPTIONS')) {
  370. $env = split(' ', getenv('RES_OPTIONS'));
  371. foreach ($env as $opt) {
  372. list($name, $val) = split(':', $opt);
  373. if ($val == '') {
  374. $val = 1;
  375. }
  376. $this->{$name} = $val;
  377. }
  378. }
  379. }
  380. /* }}} */
  381. /* Net_DNS_Resolver::string() {{{ */
  382. /**
  383. * Builds a string containing the current state of the resolver
  384. *
  385. * Builds formatted string containing the state of the resolver library suited
  386. * for display.
  387. *
  388. * @access public
  389. */
  390. function string()
  391. {
  392. $state = ";; Net_DNS_Resolver state:\n";
  393. $state .= ';; domain = ' . $this->domain . "\n";
  394. $state .= ';; searchlist = ' . implode(' ', $this->searchlist) . "\n";
  395. $state .= ';; nameservers = ' . implode(' ', $this->nameservers) . "\n";
  396. $state .= ';; port = ' . $this->port . "\n";
  397. $state .= ';; tcp_timeout = ';
  398. $state .= ($this->tcp_timeout ? $this->tcp_timeout : 'indefinite') . "\n";
  399. $state .= ';; retrans = ' . $this->retrans . ' ';
  400. $state .= 'retry = ' . $this->retry . "\n";
  401. $state .= ';; usevc = ' . $this->usevc . ' ';
  402. $state .= 'stayopen = ' . $this->stayopen . ' ';
  403. $state .= 'igntc = ' . $this->igntc . "\n";
  404. $state .= ';; defnames = ' . $this->defnames . ' ';
  405. $state .= 'dnsrch = ' . $this->dnsrch . "\n";
  406. $state .= ';; recurse = ' . $this->recurse . ' ';
  407. $state .= 'debug = ' . $this->debug . "\n";
  408. return($state);
  409. }
  410. /* }}} */
  411. /* Net_DNS_Resolver::nextid() {{{ */
  412. /**
  413. * Returns the next request Id to be used for the DNS packet header
  414. */
  415. function nextid()
  416. {
  417. global $_Net_DNS_packet_id;
  418. return($_Net_DNS_packet_id++);
  419. }
  420. /* }}} */
  421. /* not completed - Net_DNS_Resolver::nameservers() {{{ */
  422. /**
  423. * Unknown - not ported yet
  424. */
  425. function nameservers($nsa)
  426. {
  427. $defres = new Net_DNS_Resolver();
  428. if (is_array($ns)) {
  429. foreach ($nsa as $ns) {
  430. if (ereg('^[0-9]+(\.[0-9]+){0,3}$', $ns, $regs)) {
  431. $newns[count($newns)] = $ns;
  432. } else {
  433. /*
  434. * This still needs to be ported
  435. *
  436. if ($ns !~ /\./) {
  437. if (defined $defres->searchlist) {
  438. @names = map { $ns . "." . $_ }
  439. $defres->searchlist;
  440. }
  441. elsif (defined $defres->domain) {
  442. @names = ($ns . "." . $defres->domain);
  443. }
  444. }
  445. else {
  446. @names = ($ns);
  447. }
  448. my $packet = $defres->search($ns);
  449. $this->errorstring($defres->errorstring);
  450. if (defined($packet)) {
  451. push @a, cname_addr([@names], $packet);
  452. }
  453. */
  454. }
  455. }
  456. $this->nameservers = $nsa;
  457. }
  458. return($this->nameservers);
  459. }
  460. /* }}} */
  461. /* not completed - Net_DNS_Resolver::cname_addr() {{{ */
  462. /**
  463. * Unknown - not ported yet
  464. */
  465. function cname_addr()
  466. {
  467. }
  468. /* }}} */
  469. /* Net_DNS_Resolver::search() {{{ */
  470. /**
  471. * Searches nameservers for an answer
  472. *
  473. * Goes through the search list and attempts to resolve name based on
  474. * the information in the search list.
  475. *
  476. * @param string $name The name (LHS) of a resource record to query.
  477. * @param string $type The type of record to query.
  478. * @param string $class The class of record to query.
  479. * @return mixed an object of type Net_DNS_Packet on success,
  480. * or FALSE on failure.
  481. * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
  482. * @access public
  483. */
  484. function search($name, $type = 'A', $class = 'IN')
  485. {
  486. /*
  487. * If the name looks like an IP address then do an appropriate
  488. * PTR query.
  489. */
  490. if (preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $name, $regs)) {
  491. $name = "$regs[4].$regs[3].$regs[2].$regs[1].in-addr.arpa";
  492. $type = 'PTR';
  493. }
  494. /*
  495. * If the name contains at least one dot then try it as is first.
  496. */
  497. if (strchr($name, '.')) {
  498. if ($this->debug) {
  499. echo ";; search($name, $type, $class)\n";
  500. }
  501. $ans = $this->query($name, $type, $class);
  502. if ((is_object($ans)) && $ans->header->ancount > 0) {
  503. return($ans);
  504. }
  505. }
  506. /*
  507. * If the name doesn't end in a dot then apply the search list.
  508. */
  509. $domain = '';
  510. if ((! preg_match('/\.$/', $name)) && $this->dnsrch) {
  511. foreach ($this->searchlist as $domain) {
  512. $newname = "$name.$domain";
  513. if ($this->debug) {
  514. echo ";; search($newname, $type, $class)\n";
  515. }
  516. $ans = $this->query($newname, $type, $class);
  517. if ((is_object($ans)) && $ans->header->ancount > 0) {
  518. return($ans);
  519. }
  520. }
  521. }
  522. /*
  523. * Finally, if the name has no dots then try it as is.
  524. */
  525. if (! strlen(strchr($name, '.'))) {
  526. if ($this->debug) {
  527. echo ";; search($name, $type, $class)\n";
  528. }
  529. $ans = $this->query("$name.", $type, $class);
  530. if (($ans = $this->query($name, $type, $class)) &&
  531. $ans->header->ancount > 0) {
  532. return($ans);
  533. }
  534. }
  535. /*
  536. * No answer was found.
  537. */
  538. return(0);
  539. }
  540. /* }}} */
  541. /* Net_DNS_Resolver::query() {{{ */
  542. /**
  543. * Queries nameservers for an answer
  544. *
  545. * Queries the nameservers listed in the resolver configuration for an
  546. * answer to a question packet.
  547. *
  548. * @param string $name The name (LHS) of a resource record to query.
  549. * @param string $type The type of record to query.
  550. * @param string $class The class of record to query.
  551. * @return mixed an object of type Net_DNS_Packet on success,
  552. * or FALSE on failure.
  553. * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
  554. * @access public
  555. */
  556. function query($name, $type = 'A', $class = 'IN')
  557. {
  558. /*
  559. * If the name doesn't contain any dots then append the default domain.
  560. */
  561. if ((strchr($name, '.') < 0) && $this->defnames) {
  562. $name .= '.' . $this->domain;
  563. }
  564. /*
  565. * If the name looks like an IP address then do an appropriate
  566. * PTR query.
  567. */
  568. if (preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $name, $regs)) {
  569. $name = "$regs[4].$regs[3].$regs[2].$regs[1].in-addr.arpa";
  570. $type = 'PTR';
  571. }
  572. if ($this->debug) {
  573. echo ";; query($name, $type, $class)\n";
  574. }
  575. $packet = new Net_DNS_Packet($this->debug);
  576. $packet->buildQuestion($name, $type, $class);
  577. $packet->header->rd = $this->recurse;
  578. $ans = $this->send($packet);
  579. if (is_object($ans) && $ans->header->ancount > 0) {
  580. return($ans);
  581. }
  582. return(0);
  583. }
  584. /* }}} */
  585. /* Net_DNS_Resolver::send($packetORname, $qtype = '', $qclass = '') {{{ */
  586. /**
  587. * Sends a packet to a nameserver
  588. *
  589. * Determines the appropriate communication method (UDP or TCP) and
  590. * send a DNS packet to a nameserver. Use of the this function
  591. * directly is discouraged. $packetORname should always be a properly
  592. * formatted binary DNS packet. However, it is possible to send a
  593. * query here and bypass Net_DNS_Resolver::query()
  594. *
  595. * @param string $packetORname A binary DNS packet stream or a
  596. * hostname to query
  597. * @param string $qtype This should not be used
  598. * @param string $qclass This should not be used
  599. * @return object Net_DNS_Packet An answer packet object
  600. */
  601. function send($packetORname, $qtype = '', $qclass = '')
  602. {
  603. $packet = $this->make_query_packet($packetORname, $qtype, $qclass);
  604. $packet_data = $packet->data();
  605. if ($this->usevc != 0 || strlen($packet_data > 512)) {
  606. $ans = $this->send_tcp($packet, $packet_data);
  607. } else {
  608. $ans = $this->send_udp($packet, $packet_data);
  609. if ($ans && $ans->header->tc && $this->igntc != 0) {
  610. if ($this->debug) {
  611. echo ";;\n;; packet truncated: retrying using TCP\n";
  612. }
  613. $ans = $this->send_tcp($packet, $packet_data);
  614. }
  615. }
  616. return($ans);
  617. }
  618. /* }}} */
  619. /* Net_DNS_Resolver::printhex($packet_data) {{{ */
  620. /**
  621. * Sends a packet via TCP to the list of name servers.
  622. */
  623. function printhex($data)
  624. {
  625. $data = ' ' . $data;
  626. $start = 0;
  627. while ($start < strlen($data)) {
  628. printf(';; %03d: ', $start);
  629. for ($ctr = $start; $ctr < $start+16; $ctr++) {
  630. if ($ctr < strlen($data))
  631. printf('%02x ', ord($data[$ctr]));
  632. else
  633. echo ' ';
  634. }
  635. echo ' ';
  636. for ($ctr = $start; $ctr < $start+16; $ctr++) {
  637. if (ord($data[$ctr]) < 32 || ord($data[$ctr]) > 127) {
  638. echo '.';
  639. } else {
  640. echo $data[$ctr];
  641. }
  642. }
  643. echo "\n";
  644. $start += 16;
  645. }
  646. }
  647. /* }}} */
  648. /* Net_DNS_Resolver::send_tcp($packet, $packet_data) {{{ */
  649. /**
  650. * Sends a packet via TCP to the list of name servers.
  651. *
  652. * @param string $packet A packet object to send to the NS list
  653. * @param string $packet_data The data in the packet as returned by
  654. * the Net_DNS_Packet::data() method
  655. * @return object Net_DNS_Packet Returns an answer packet object
  656. * @see Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send()
  657. */
  658. function send_tcp($packet, $packet_data)
  659. {
  660. if (! count($this->nameservers)) {
  661. $this->errorstring = 'no nameservers';
  662. if ($this->debug) {
  663. echo ";; ERROR: send_tcp: no nameservers\n";
  664. }
  665. return(NULL);
  666. }
  667. $timeout = $this->tcp_timeout;
  668. foreach ($this->nameservers as $ns) {
  669. $dstport = $this->port;
  670. if ($this->debug) {
  671. echo ";; send_tcp($ns:$dstport)\n";
  672. }
  673. $sock_key = "$ns:$dstport";
  674. if (isset($this->sockets[$sock_key]) && is_resource($this->sockets[$sock_key])) {
  675. $sock = &$this->sockets[$sock_key];
  676. } else {
  677. if (! ($sock = @fsockopen($ns, $dstport, $errno,
  678. $errstr, $timeout))) {
  679. $this->errorstring = 'connection failed';
  680. if ($this->debug) {
  681. echo ";; ERROR: send_tcp: connection failed: $errstr\n";
  682. }
  683. continue;
  684. }
  685. $this->sockets[$sock_key] = $sock;
  686. unset($sock);
  687. $sock = &$this->sockets[$sock_key];
  688. }
  689. $lenmsg = pack('n', strlen($packet_data));
  690. if ($this->debug) {
  691. echo ';; sending ' . strlen($packet_data) . " bytes\n";
  692. }
  693. if (($sent = fwrite($sock, $lenmsg)) == -1) {
  694. $this->errorstring = 'length send failed';
  695. if ($this->debug) {
  696. echo ";; ERROR: send_tcp: length send failed\n";
  697. }
  698. continue;
  699. }
  700. if (($sent = fwrite($sock, $packet_data)) == -1) {
  701. $this->errorstring = 'packet send failed';
  702. if ($this->debug) {
  703. echo ";; ERROR: send_tcp: packet data send failed\n";
  704. }
  705. }
  706. socket_set_timeout($sock, $timeout);
  707. $buf = fread($sock, 2);
  708. $e = socket_get_status($sock);
  709. /* If $buf is empty, we want to supress errors
  710. long enough to reach the continue; down the line */
  711. $len = @unpack('nint', $buf);
  712. $len = @$len['int'];
  713. if (!$len) {
  714. continue;
  715. }
  716. $buf = fread($sock, $len);
  717. $actual = strlen($buf);
  718. $this->answerfrom = $ns;
  719. $this->answersize = $len;
  720. if ($this->debug) {
  721. echo ";; received $actual bytes\n";
  722. }
  723. if ($actual != $len) {
  724. $this->errorstring = "expected $len bytes, received $buf";
  725. if ($this->debug) {
  726. echo ';; send_tcp: ' . $this->errorstring;
  727. }
  728. continue;
  729. }
  730. $ans = new Net_DNS_Packet($this->debug);
  731. if (is_null($ans->parse($buf))) {
  732. continue;
  733. }
  734. $this->errorstring = $ans->header->rcode;
  735. $ans->answerfrom = $this->answerfrom;
  736. $ans->answersize = $this->answersize;
  737. return($ans);
  738. }
  739. }
  740. /* }}} */
  741. /* Net_DNS_Resolver::send_udp_no_sock_lib($packet, $packet_data) {{{ */
  742. /**
  743. * Sends a packet via UDP to the list of name servers.
  744. *
  745. * This function sends a packet to a nameserver. It is called by
  746. * send_udp if the sockets PHP extension is not compiled into PHP.
  747. *
  748. * @param string $packet A packet object to send to the NS list
  749. * @param string $packet_data The data in the packet as returned by
  750. * the Net_DNS_Packet::data() method
  751. * @return object Net_DNS_Packet Returns an answer packet object
  752. * @see Net_DNS_Resolver::send_tcp(), Net_DNS_Resolver::send(),
  753. * Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send_udp_with_sock_lib()
  754. */
  755. function send_udp_no_sock_lib($packet, $packet_data)
  756. {
  757. $retrans = $this->retrans;
  758. $timeout = $retrans;
  759. /*
  760. * PHP doesn't have excellent socket support as of this writing.
  761. * This needs to be rewritten when PHP POSIX socket support is
  762. * complete.
  763. * Obviously, this code is MUCH different than the PERL implementation
  764. */
  765. $w = error_reporting(0);
  766. $ctr = 0;
  767. // Create a socket handle for each nameserver
  768. foreach ($this->nameservers as $nameserver) {
  769. if ($sock[$ctr++] = fsockopen("udp://$nameserver", $this->port)) {
  770. $peerhost[$ctr-1] = $nameserver;
  771. $peerport[$ctr-1] = $this->port;
  772. socket_set_blocking($sock, FALSE);
  773. } else {
  774. $ctr--;
  775. }
  776. }
  777. error_reporting($w);
  778. if ($ctr == 0) {
  779. $this->errorstring = 'no nameservers';
  780. return(NULL);
  781. }
  782. for ($i = 0; $i < $this->retry; $i++, $retrans *= 2,
  783. $timeout = (int) ($retrans / (count($ns)+1))) {
  784. if ($timeout < 1) {
  785. $timeout = 1;
  786. }
  787. foreach ($sock as $k => $s) {
  788. if ($this->debug) {
  789. echo ';; send_udp(' . $peerhost[$k] . ':' . $peerport[$k] . '): sending ' . strlen($packet_data) . " bytes\n";
  790. }
  791. if (! fwrite($s, $packet_data)) {
  792. if ($this->debug) {
  793. echo ";; send error\n";
  794. }
  795. }
  796. /*
  797. * Here's where it get's really nasty. We don't have a select()
  798. * function here, so we have to poll for a response... UGH!
  799. */
  800. $timetoTO = time() + (double)microtime() + $timeout;
  801. /*
  802. * let's sleep for a few hundred microseconds to let the
  803. * data come in from the network...
  804. */
  805. usleep(500);
  806. $buf = '';
  807. while (! strlen($buf) && $timetoTO > (time() +
  808. (double)microtime())) {
  809. socket_set_blocking($s, FALSE);
  810. if ($buf = fread($s, 512)) {
  811. $this->answerfrom = $peerhost[$k];
  812. $this->answersize = strlen($buf);
  813. if ($this->debug) {
  814. echo ';; answer from ' . $peerhost[$k] . ':' .
  815. $peerport[$k] . ': ' . strlen($buf) . " bytes\n";
  816. }
  817. $ans = new Net_DNS_Packet($this->debug);
  818. if ($ans->parse($buf)) {
  819. if ($ans->header->qr != '1') {
  820. continue;
  821. }
  822. if ($ans->header->id != $packet->header->id) {
  823. continue;
  824. }
  825. $this->errorstring = $ans->header->rcode;
  826. $ans->answerfrom = $this->answerfrom;
  827. $ans->answersize = $this->answersize;
  828. return($ans);
  829. }
  830. }
  831. // Sleep another 1/100th of a second... this sucks...
  832. usleep(1000);
  833. }
  834. $this->errorstring = 'query timed out';
  835. return(NULL);
  836. }
  837. }
  838. }
  839. /* }}} */
  840. /* Net_DNS_Resolver::send_udp_with_sock_lib($packet, $packet_data) {{{ */
  841. /**
  842. * Sends a packet via UDP to the list of name servers.
  843. *
  844. * This function sends a packet to a nameserver. It is called by
  845. * send_udp if the sockets PHP extension is compiled into PHP.
  846. *
  847. * @param string $packet A packet object to send to the NS list
  848. * @param string $packet_data The data in the packet as returned by
  849. * the Net_DNS_Packet::data() method
  850. * @return object Net_DNS_Packet Returns an answer packet object
  851. * @see Net_DNS_Resolver::send_tcp(), Net_DNS_Resolver::send(),
  852. * Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send_udp_no_sock_lib()
  853. */
  854. function send_udp_with_sock_lib($packet, $packet_data)
  855. {
  856. $retrans = $this->retrans;
  857. $timeout = $retrans;
  858. //$w = error_reporting(0);
  859. $ctr = 0;
  860. // Create a socket handle for each nameserver
  861. foreach ($this->nameservers as $nameserver) {
  862. if ((($sock[$ctr++] = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) >= 0) &&
  863. socket_connect($sock[$ctr-1], $nameserver, $this->port) >= 0) {
  864. $peerhost[$ctr-1] = $nameserver;
  865. $peerport[$ctr-1] = $this->port;
  866. socket_set_nonblock($sock[$ctr-1]);
  867. } else {
  868. $ctr--;
  869. }
  870. }
  871. //error_reporting($w);
  872. if ($ctr == 0) {
  873. $this->errorstring = 'no nameservers';
  874. return(NULL);
  875. }
  876. for ($i = 0; $i < $this->retry; $i++, $retrans *= 2,
  877. $timeout = (int) ($retrans / (count($ns)+1))) {
  878. if ($timeout < 1) {
  879. $timeout = 1;
  880. }
  881. foreach ($sock as $k => $s) {
  882. if ($this->debug) {
  883. echo ';; send_udp(' . $peerhost[$k] . ':' . $peerport[$k] . '): sending ' . strlen($packet_data) . " bytes\n";
  884. }
  885. if (! socket_write($s, $packet_data)) {
  886. if ($this->debug) {
  887. echo ";; send error\n";
  888. }
  889. }
  890. $set = array($s);
  891. if ($this->debug) {
  892. echo ";; timeout set to $timeout seconds\n";
  893. }
  894. $changed = socket_select($set, $w = null, $e = null, $timeout);
  895. if ($changed) {
  896. $buf = socket_read($set[0], 512);
  897. $this->answerfrom = $peerhost[$k];
  898. $this->answersize = strlen($buf);
  899. if ($this->debug) {
  900. echo ';; answer from ' . $peerhost[$k] . ':' .
  901. $peerport[$k] . ': ' . strlen($buf) . " bytes\n";
  902. }
  903. $ans = new Net_DNS_Packet($this->debug);
  904. if ($ans->parse($buf)) {
  905. if ($ans->header->qr != '1') {
  906. continue;
  907. }
  908. if ($ans->header->id != $packet->header->id) {
  909. continue;
  910. }
  911. $this->errorstring = $ans->header->rcode;
  912. $ans->answerfrom = $this->answerfrom;
  913. $ans->answersize = $this->answersize;
  914. return($ans);
  915. }
  916. }
  917. $this->errorstring = 'query timed out';
  918. return(NULL);
  919. }
  920. }
  921. }
  922. /* }}} */
  923. /* Net_DNS_Resolver::send_udp($packet, $packet_data) {{{ */
  924. /**
  925. * Sends a packet via UDP to the list of name servers.
  926. *
  927. * This function sends a packet to a nameserver. send_udp calls
  928. * either Net_DNS_Resolver::send_udp_no_sock_lib() or
  929. * Net_DNS_Resolver::send_udp_with_sock_lib() depending on whether or
  930. * not the sockets extension is compiled into PHP. Note that using the
  931. * sockets extension is MUCH more effecient.
  932. *
  933. * @param object Net_DNS_Packet $packet A packet object to send to the NS list
  934. * @param string $packet_data The data in the packet as returned by
  935. * the Net_DNS_Packet::data() method
  936. * @return object Net_DNS_Packet Returns an answer packet object
  937. * @see Net_DNS_Resolver::send_tcp(), Net_DNS_Resolver::send(),
  938. * Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send_udp_no_sock_lib()
  939. */
  940. function send_udp($packet, $packet_data)
  941. {
  942. if (extension_loaded('sockets') && $this->useEnhancedSockets) {
  943. if ($this->debug) {
  944. echo "\n;; using extended PHP sockets\n";
  945. }
  946. return($this->send_udp_with_sock_lib($packet, $packet_data));
  947. } else {
  948. if ($this->debug) {
  949. echo "\n;; using simple sockets\n";
  950. }
  951. return($this->send_udp_no_sock_lib($packet, $packet_data));
  952. }
  953. }
  954. /* }}} */
  955. /* Net_DNS_Resolver::make_query_packet($packetORname, $type = '', $class = '') {{{ */
  956. /**
  957. * Unknown
  958. */
  959. function make_query_packet($packetORname, $type = '', $class = '')
  960. {
  961. if (is_object($packetORname) && get_class($packetORname) == 'net_dns_packet') {
  962. $packet = $packetORname;
  963. } else {
  964. $name = $packetORname;
  965. if ($type == '') {
  966. $type = 'A';
  967. }
  968. if ($class == '') {
  969. $class = 'IN';
  970. }
  971. /*
  972. * If the name looks like an IP address then do an appropriate
  973. * PTR query.
  974. */
  975. if (preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $name, $regs)) {
  976. $name = "$regs[4].$regs[3].$regs[2].$regs[1].in-addr.arpa";
  977. $type = 'PTR';
  978. }
  979. if ($this->debug) {
  980. echo ";; query($name, $type, $class)\n";
  981. }
  982. $packet = new Net_DNS_Packet($this->debug);
  983. $packet->buildQuestion($name, $type, $class);
  984. }
  985. $packet->header->rd = $this->recurse;
  986. return($packet);
  987. }
  988. /* }}} */
  989. /* Net_DNS_Resolver::axfr_old($dname, $class = 'IN') {{{ */
  990. /**
  991. * Performs an AXFR query (zone transfer) (OLD BUGGY STYLE)
  992. *
  993. * This is deprecated and should not be used!
  994. *
  995. * @param string $dname The domain (zone) to transfer
  996. * @param string $class The class in which to look for the zone.
  997. * @return object Net_DNS_Packet
  998. * @access public
  999. */
  1000. function axfr_old($dname, $class = 'IN')
  1001. {
  1002. return($this->axfr($dname, $class, TRUE));
  1003. }
  1004. /* }}} */
  1005. /* Net_DNS_Resolver::axfr($dname, $class = 'IN', $old = FALSE) {{{ */
  1006. /**
  1007. * Performs an AXFR query (zone transfer)
  1008. *
  1009. * Requests a zone transfer from the nameservers. Note that zone
  1010. * transfers will ALWAYS use TCP regardless of the setting of the
  1011. * Net_DNS_Resolver::$usevc flag. If $old is set to TRUE, Net_DNS requires
  1012. * a nameserver that supports the many-answers style transfer format. Large
  1013. * zone transfers will not function properly. Setting $old to TRUE is _NOT_
  1014. * recommended and should only be used for backwards compatibility.
  1015. *
  1016. * @param string $dname The domain (zone) to transfer
  1017. * @param string $class The class in which to look for the zone.
  1018. * @param boolean $old Requires 'old' style many-answer format to function. Used for backwards compatibility only.
  1019. * @return object Net_DNS_Packet
  1020. * @access public
  1021. */
  1022. function axfr($dname, $class = 'IN', $old = FALSE)
  1023. {
  1024. if ($old) {
  1025. if ($this->debug) {
  1026. echo ";; axfr_start($dname, $class)\n";
  1027. }
  1028. if (! count($this->nameservers)) {
  1029. $this->errorstring = 'no nameservers';
  1030. if ($this->debug) {
  1031. echo ";; ERROR: no nameservers\n";
  1032. }
  1033. return(NULL);
  1034. }
  1035. $packet = $this->make_query_packet($dname, 'AXFR', $class);
  1036. $packet_data = $packet->data();
  1037. $ans = $this->send_tcp($packet, $packet_data);
  1038. return($ans);
  1039. } else {
  1040. if ($this->axfr_start($dname, $class) === NULL) {
  1041. return(NULL);
  1042. }
  1043. $ret = array();
  1044. while (($ans = $this->axfr_next()) !== NULL) {
  1045. if ($ans === NULL) {
  1046. return(NULL);
  1047. }
  1048. array_push($ret, $ans);
  1049. }
  1050. return($ret);
  1051. }
  1052. }
  1053. /* }}} */
  1054. /* Net_DNS_Resolver::axfr_start($dname, $class = 'IN') {{{ */
  1055. /**
  1056. * Sends a packet via TCP to the list of name servers.
  1057. *
  1058. * @param string $packet A packet object to send to the NS list
  1059. * @param string $packet_data The data in the packet as returned by
  1060. * the Net_DNS_Packet::data() method
  1061. * @return object Net_DNS_Packet Returns an answer packet object
  1062. * @see Net_DNS_Resolver::send_tcp()
  1063. */
  1064. function axfr_start($dname, $class = 'IN')
  1065. {
  1066. if ($this->debug) {
  1067. echo ";; axfr_start($dname, $class)\n";
  1068. }
  1069. if (! count($this->nameservers)) {
  1070. $this->errorstring = "no nameservers";
  1071. if ($this->debug) {
  1072. echo ";; ERROR: axfr_start: no nameservers\n";
  1073. }
  1074. return(NULL);
  1075. }
  1076. $packet = $this->make_query_packet($dname, "AXFR", $class);
  1077. $packet_data = $packet->data();
  1078. $timeout = $this->tcp_timeout;
  1079. foreach ($this->nameservers as $ns) {
  1080. $dstport = $this->port;
  1081. if ($this->debug) {
  1082. echo ";; axfr_start($ns:$dstport)\n";
  1083. }
  1084. $sock_key = "$ns:$dstport";
  1085. if (is_resource($this->sockets[$sock_key])) {
  1086. $sock = &$this->sockets[$sock_key];
  1087. } else {
  1088. if (! ($sock = fsockopen($ns, $dstport, $errno,
  1089. $errstr, $timeout))) {
  1090. $this->errorstring = "connection failed";
  1091. if ($this->debug) {
  1092. echo ";; ERROR: axfr_start: connection failed: $errstr\n";
  1093. }
  1094. continue;
  1095. }
  1096. $this->sockets[$sock_key] = $sock;
  1097. unset($sock);
  1098. $sock = &$this->sockets[$sock_key];
  1099. }
  1100. $lenmsg = pack("n", strlen($packet_data));
  1101. if ($this->debug) {
  1102. echo ";; sending " . strlen($packet_data) . " bytes\n";
  1103. }
  1104. if (($sent = fwrite($sock, $lenmsg)) == -1) {
  1105. $this->errorstring = "length send failed";
  1106. if ($this->debug) {
  1107. echo ";; ERROR: axfr_start: length send failed\n";
  1108. }
  1109. continue;
  1110. }
  1111. if (($sent = fwrite($sock, $packet_data)) == -1) {
  1112. $this->errorstring = "packet send failed";
  1113. if ($this->debug) {
  1114. echo ";; ERROR: axfr_start: packet data send failed\n";
  1115. }
  1116. }
  1117. socket_set_timeout($sock, $timeout);
  1118. $this->_axfr_sock = $sock;
  1119. $this->_axfr_rr = array();
  1120. $this->_axfr_soa_count = 0;
  1121. return($sock);
  1122. }
  1123. }
  1124. /* }}} */
  1125. /* Net_DNS_Resolver::axfr_next() {{{ */
  1126. /**
  1127. * Requests the next RR from a existing transfer started with axfr_start
  1128. *
  1129. * @return object Net_DNS_RR Returns a Net_DNS_RR object of the next RR
  1130. * from a zone transfer.
  1131. * @see Net_DNS_Resolver::send_tcp()
  1132. */
  1133. function axfr_next()
  1134. {
  1135. if (! count($this->_axfr_rr)) {
  1136. if (! isset($this->_axfr_sock) || ! is_resource($this->_axfr_sock)) {
  1137. $this->errorstring = 'no zone transfer in progress';
  1138. return(NULL);
  1139. }
  1140. $timeout = $this->tcp_timeout;
  1141. $buf = $this->read_tcp($this->_axfr_sock, 2, $this->debug);
  1142. if (! strlen($buf)) {
  1143. $this->errorstring = 'truncated zone transfer';
  1144. return(NULL);
  1145. }
  1146. $len = unpack('n1len', $buf);
  1147. $len = $len['len'];
  1148. if (! $len) {
  1149. $this->errorstring = 'truncated zone transfer';
  1150. return(NULL);
  1151. }
  1152. $buf = $this->read_tcp($this->_axfr_sock, $len, $this->debug);
  1153. if ($this->debug) {
  1154. echo ';; received ' . strlen($buf) . "bytes\n";
  1155. }
  1156. if (strlen($buf) != $len) {
  1157. $this->errorstring = 'expected ' . $len . ' bytes, received ' . strlen($buf);
  1158. if ($this->debug) {
  1159. echo ';; ' . $err . "\n";
  1160. }
  1161. return(NULL);
  1162. }
  1163. $ans = new Net_DNS_Packet($this->debug);
  1164. if (! $ans->parse($buf)) {
  1165. if (! $this->errorstring) {
  1166. $this->errorstring = 'unknown error during packet parsing';
  1167. }
  1168. return(NULL);
  1169. }
  1170. if ($ans->header->ancount < 1) {
  1171. $this->errorstring = 'truncated zone transfer';
  1172. return(NULL);
  1173. }
  1174. if ($ans->header->rcode != 'NOERROR') {
  1175. $this->errorstring = 'errorcode ' . $ans->header->rcode . ' returned';
  1176. return(NULL);
  1177. }
  1178. foreach ($ans->answer as $rr) {
  1179. if ($rr->type == 'SOA') {
  1180. if (++$this->_axfr_soa_count < 2) {
  1181. array_push($this->_axfr_rr, $rr);
  1182. }
  1183. } else {
  1184. array_push($this->_axfr_rr, $rr);
  1185. }
  1186. }
  1187. if ($this->_axfr_soa_count >= 2) {
  1188. unset($this->_axfr_sock);
  1189. }
  1190. }
  1191. $rr = array_shift($this->_axfr_rr);
  1192. return($rr);
  1193. }
  1194. /* }}} */
  1195. /* Net_DNS_Resolver::read_tcp() {{{ */
  1196. /**
  1197. * Unknown - not ported yet
  1198. */
  1199. function read_tcp($sock, $nbytes, $debug = 0)
  1200. {
  1201. $buf = '';
  1202. while (strlen($buf) < $nbytes) {
  1203. $nread = $nbytes - strlen($buf);
  1204. $read_buf = '';
  1205. if ($debug) {
  1206. echo ";; read_tcp: expecting $nread bytes\n";
  1207. }
  1208. $read_buf = fread($sock, $nread);
  1209. if (! strlen($read_buf)) {
  1210. if ($debug) {
  1211. echo ";; ERROR: read_tcp: fread failed\n";
  1212. }
  1213. break;
  1214. }
  1215. if ($debug) {
  1216. echo ';; read_tcp: received ' . strlen($read_buf) . " bytes\n";
  1217. }
  1218. if (!strlen($read_buf)) {
  1219. break;
  1220. }
  1221. $buf .= $read_buf;
  1222. }
  1223. return($buf);
  1224. }
  1225. /* }}} */
  1226. }
  1227. /* }}} */
  1228. /* VIM settings {{{
  1229. * Local variables:
  1230. * tab-width: 4
  1231. * c-basic-offset: 4
  1232. * soft-stop-width: 4
  1233. * c indent on
  1234. * expandtab on
  1235. * End:
  1236. * vim600: sw=4 ts=4 sts=4 cindent fdm=marker et
  1237. * vim<600: sw=4 ts=4
  1238. * }}} */
  1239. ?>