| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- <?php
- namespace React\Tests\Dns\Query;
- use React\Dns\Model\Message;
- use React\Dns\Protocol\BinaryDumper;
- use React\Dns\Protocol\Parser;
- use React\Dns\Query\Query;
- use React\Dns\Query\UdpTransportExecutor;
- use React\EventLoop\Loop;
- use React\Tests\Dns\TestCase;
- class UdpTransportExecutorTest extends TestCase
- {
- /**
- * @dataProvider provideDefaultPortProvider
- * @param string $input
- * @param string $expected
- */
- public function testCtorShouldAcceptNameserverAddresses($input, $expected)
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $executor = new UdpTransportExecutor($input, $loop);
- $ref = new \ReflectionProperty($executor, 'nameserver');
- $ref->setAccessible(true);
- $value = $ref->getValue($executor);
- $this->assertEquals($expected, $value);
- }
- public static function provideDefaultPortProvider()
- {
- return array(
- array(
- '8.8.8.8',
- 'udp://8.8.8.8:53'
- ),
- array(
- '1.2.3.4:5',
- 'udp://1.2.3.4:5'
- ),
- array(
- 'udp://1.2.3.4',
- 'udp://1.2.3.4:53'
- ),
- array(
- 'udp://1.2.3.4:53',
- 'udp://1.2.3.4:53'
- ),
- array(
- '::1',
- 'udp://[::1]:53'
- ),
- array(
- '[::1]:53',
- 'udp://[::1]:53'
- )
- );
- }
- public function testCtorWithoutLoopShouldAssignDefaultLoop()
- {
- $executor = new UdpTransportExecutor('127.0.0.1');
- $ref = new \ReflectionProperty($executor, 'loop');
- $ref->setAccessible(true);
- $loop = $ref->getValue($executor);
- $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop);
- }
- public function testCtorShouldThrowWhenNameserverAddressIsInvalid()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $this->setExpectedException('InvalidArgumentException');
- new UdpTransportExecutor('///', $loop);
- }
- public function testCtorShouldThrowWhenNameserverAddressContainsHostname()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $this->setExpectedException('InvalidArgumentException');
- new UdpTransportExecutor('localhost', $loop);
- }
- public function testCtorShouldThrowWhenNameserverSchemeIsInvalid()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $this->setExpectedException('InvalidArgumentException');
- new UdpTransportExecutor('tcp://1.2.3.4', $loop);
- }
- public function testQueryRejectsIfMessageExceedsUdpSize()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addReadStream');
- $executor = new UdpTransportExecutor('8.8.8.8:53', $loop);
- $query = new Query('google.' . str_repeat('.com', 200), Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
- $exception = null;
- $promise->then(null, function ($reason) use (&$exception) {
- $exception = $reason;
- });
- $this->setExpectedException(
- 'RuntimeException',
- 'DNS query for ' . $query->name . ' (A) failed: Query too large for UDP transport',
- defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90
- );
- throw $exception;
- }
- public function testQueryRejectsIfServerConnectionFails()
- {
- if (defined('HHVM_VERSION')) {
- $this->markTestSkipped('HHVM reports different error message for invalid addresses');
- }
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addReadStream');
- $executor = new UdpTransportExecutor('::1', $loop);
- $ref = new \ReflectionProperty($executor, 'nameserver');
- $ref->setAccessible(true);
- $ref->setValue($executor, '///');
- $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
- $exception = null;
- $promise->then(null, function ($reason) use (&$exception) {
- $exception = $reason;
- });
- $this->setExpectedException(
- 'RuntimeException',
- 'DNS query for google.com (A) failed: Unable to connect to DNS server /// (Failed to parse address "///")'
- );
- throw $exception;
- }
- public function testQueryRejectsIfSendToServerFailsAfterConnectionWithoutCallingCustomErrorHandler()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addReadStream');
- $executor = new UdpTransportExecutor('0.0.0.0', $loop);
- // increase hard-coded maximum packet size to allow sending excessive data
- $ref = new \ReflectionProperty($executor, 'maxPacketSize');
- $ref->setAccessible(true);
- $ref->setValue($executor, PHP_INT_MAX);
- $error = null;
- set_error_handler(function ($_, $errstr) use (&$error) {
- $error = $errstr;
- });
- $query = new Query(str_repeat('a.', 100000) . '.example', Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- restore_error_handler();
- $this->assertNull($error);
- $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
- $exception = null;
- $promise->then(null, function ($reason) use (&$exception) {
- $exception = $reason;
- });
- // ECONNREFUSED (Connection refused) on Linux, EMSGSIZE (Message too long) on macOS
- $this->setExpectedException(
- 'RuntimeException',
- 'DNS query for ' . $query->name . ' (A) failed: Unable to send query to DNS server udp://0.0.0.0:53 ('
- );
- throw $exception;
- }
- public function testQueryKeepsPendingIfReadFailsBecauseServerRefusesConnection()
- {
- $socket = null;
- $callback = null;
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addReadStream')->with($this->callback(function ($ref) use (&$socket) {
- $socket = $ref;
- return true;
- }), $this->callback(function ($ref) use (&$callback) {
- $callback = $ref;
- return true;
- }));
- $executor = new UdpTransportExecutor('0.0.0.0', $loop);
- $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- $this->assertNotNull($socket);
- $callback($socket);
- $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
- $pending = true;
- $promise->then(function () use (&$pending) {
- $pending = false;
- }, function () use (&$pending) {
- $pending = false;
- });
- $this->assertTrue($pending);
- }
- /**
- * @group internet
- */
- public function testQueryRejectsOnCancellation()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addReadStream');
- $loop->expects($this->once())->method('removeReadStream');
- $executor = new UdpTransportExecutor('8.8.8.8:53', $loop);
- $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- $promise->cancel();
- $exception = null;
- $promise->then(null, function ($reason) use (&$exception) {
- $exception = $reason;
- });
- /** @var \React\Dns\Query\CancellationException $exception */
- $this->assertInstanceOf('React\Dns\Query\CancellationException', $exception);
- $this->assertEquals('DNS query for google.com (A) has been cancelled', $exception->getMessage());
- }
- public function testQueryKeepsPendingIfServerSendsInvalidMessage()
- {
- $server = stream_socket_server('udp://127.0.0.1:0', $errno, $errstr, STREAM_SERVER_BIND);
- Loop::addReadStream($server, function ($server) {
- $data = stream_socket_recvfrom($server, 512, 0, $peer);
- stream_socket_sendto($server, 'invalid', 0, $peer);
- Loop::removeReadStream($server);
- fclose($server);
- });
- $address = stream_socket_get_name($server, false);
- $executor = new UdpTransportExecutor($address);
- $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN);
- $wait = true;
- $promise = $executor->query($query)->then(
- null,
- function ($e) use (&$wait) {
- $wait = false;
- }
- );
- \React\Async\await(\React\Promise\Timer\sleep(0.2));
- $this->assertTrue($wait);
- $promise->cancel();
- }
- public function testQueryKeepsPendingIfServerSendsInvalidId()
- {
- $parser = new Parser();
- $dumper = new BinaryDumper();
- $server = stream_socket_server('udp://127.0.0.1:0', $errno, $errstr, STREAM_SERVER_BIND);
- Loop::addReadStream($server, function ($server) use ($parser, $dumper) {
- $data = stream_socket_recvfrom($server, 512, 0, $peer);
- $message = $parser->parseMessage($data);
- $message->id = 0;
- stream_socket_sendto($server, $dumper->toBinary($message), 0, $peer);
- Loop::removeReadStream($server);
- fclose($server);
- });
- $address = stream_socket_get_name($server, false);
- $executor = new UdpTransportExecutor($address);
- $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN);
- $wait = true;
- $promise = $executor->query($query)->then(
- null,
- function ($e) use (&$wait) {
- $wait = false;
- }
- );
- \React\Async\await(\React\Promise\Timer\sleep(0.2));
- $this->assertTrue($wait);
- $promise->cancel();
- }
- public function testQueryRejectsIfServerSendsTruncatedResponse()
- {
- $parser = new Parser();
- $dumper = new BinaryDumper();
- $server = stream_socket_server('udp://127.0.0.1:0', $errno, $errstr, STREAM_SERVER_BIND);
- Loop::addReadStream($server, function ($server) use ($parser, $dumper) {
- $data = stream_socket_recvfrom($server, 512, 0, $peer);
- $message = $parser->parseMessage($data);
- $message->tc = true;
- stream_socket_sendto($server, $dumper->toBinary($message), 0, $peer);
- Loop::removeReadStream($server);
- fclose($server);
- });
- $address = stream_socket_get_name($server, false);
- $executor = new UdpTransportExecutor($address);
- $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- $this->setExpectedException(
- 'RuntimeException',
- 'DNS query for google.com (A) failed: The DNS server udp://' . $address . ' returned a truncated result for a UDP query',
- defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90
- );
- \React\Async\await(\React\Promise\Timer\timeout($promise, 0.1));
- }
- public function testQueryResolvesIfServerSendsValidResponse()
- {
- $parser = new Parser();
- $dumper = new BinaryDumper();
- $server = stream_socket_server('udp://127.0.0.1:0', $errno, $errstr, STREAM_SERVER_BIND);
- Loop::addReadStream($server, function ($server) use ($parser, $dumper) {
- $data = stream_socket_recvfrom($server, 512, 0, $peer);
- $message = $parser->parseMessage($data);
- stream_socket_sendto($server, $dumper->toBinary($message), 0, $peer);
- Loop::removeReadStream($server);
- fclose($server);
- });
- $address = stream_socket_get_name($server, false);
- $executor = new UdpTransportExecutor($address);
- $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN);
- $promise = $executor->query($query);
- $response = \React\Async\await(\React\Promise\Timer\timeout($promise, 0.2));
- $this->assertInstanceOf('React\Dns\Model\Message', $response);
- }
- }
|