1<?php 2 3namespace React\Dns\Resolver; 4 5use React\Cache\ArrayCache; 6use React\Cache\CacheInterface; 7use React\Dns\Config\HostsFile; 8use React\Dns\Query\CachingExecutor; 9use React\Dns\Query\CoopExecutor; 10use React\Dns\Query\ExecutorInterface; 11use React\Dns\Query\HostsFileExecutor; 12use React\Dns\Query\RetryExecutor; 13use React\Dns\Query\SelectiveTransportExecutor; 14use React\Dns\Query\TcpTransportExecutor; 15use React\Dns\Query\TimeoutExecutor; 16use React\Dns\Query\UdpTransportExecutor; 17use React\EventLoop\LoopInterface; 18 19final class Factory 20{ 21 /** 22 * @param string $nameserver 23 * @param LoopInterface $loop 24 * @return \React\Dns\Resolver\ResolverInterface 25 */ 26 public function create($nameserver, LoopInterface $loop) 27 { 28 $executor = $this->decorateHostsFileExecutor($this->createExecutor($nameserver, $loop)); 29 30 return new Resolver($executor); 31 } 32 33 /** 34 * @param string $nameserver 35 * @param LoopInterface $loop 36 * @param ?CacheInterface $cache 37 * @return \React\Dns\Resolver\ResolverInterface 38 */ 39 public function createCached($nameserver, LoopInterface $loop, CacheInterface $cache = null) 40 { 41 // default to keeping maximum of 256 responses in cache unless explicitly given 42 if (!($cache instanceof CacheInterface)) { 43 $cache = new ArrayCache(256); 44 } 45 46 $executor = $this->createExecutor($nameserver, $loop); 47 $executor = new CachingExecutor($executor, $cache); 48 $executor = $this->decorateHostsFileExecutor($executor); 49 50 return new Resolver($executor); 51 } 52 53 /** 54 * Tries to load the hosts file and decorates the given executor on success 55 * 56 * @param ExecutorInterface $executor 57 * @return ExecutorInterface 58 * @codeCoverageIgnore 59 */ 60 private function decorateHostsFileExecutor(ExecutorInterface $executor) 61 { 62 try { 63 $executor = new HostsFileExecutor( 64 HostsFile::loadFromPathBlocking(), 65 $executor 66 ); 67 } catch (\RuntimeException $e) { 68 // ignore this file if it can not be loaded 69 } 70 71 // Windows does not store localhost in hosts file by default but handles this internally 72 // To compensate for this, we explicitly use hard-coded defaults for localhost 73 if (DIRECTORY_SEPARATOR === '\\') { 74 $executor = new HostsFileExecutor( 75 new HostsFile("127.0.0.1 localhost\n::1 localhost"), 76 $executor 77 ); 78 } 79 80 return $executor; 81 } 82 83 private function createExecutor($nameserver, LoopInterface $loop) 84 { 85 $parts = \parse_url($nameserver); 86 87 if (isset($parts['scheme']) && $parts['scheme'] === 'tcp') { 88 $executor = $this->createTcpExecutor($nameserver, $loop); 89 } elseif (isset($parts['scheme']) && $parts['scheme'] === 'udp') { 90 $executor = $this->createUdpExecutor($nameserver, $loop); 91 } else { 92 $executor = new SelectiveTransportExecutor( 93 $this->createUdpExecutor($nameserver, $loop), 94 $this->createTcpExecutor($nameserver, $loop) 95 ); 96 } 97 98 return new CoopExecutor($executor); 99 } 100 101 private function createTcpExecutor($nameserver, LoopInterface $loop) 102 { 103 return new TimeoutExecutor( 104 new TcpTransportExecutor($nameserver, $loop), 105 5.0, 106 $loop 107 ); 108 } 109 110 private function createUdpExecutor($nameserver, LoopInterface $loop) 111 { 112 return new RetryExecutor( 113 new TimeoutExecutor( 114 new UdpTransportExecutor( 115 $nameserver, 116 $loop 117 ), 118 5.0, 119 $loop 120 ) 121 ); 122 } 123} 124