diff --git a/examples/custom-config.php b/examples/custom-config.php index c0c6189..17f9a3d 100644 --- a/examples/custom-config.php +++ b/examples/custom-config.php @@ -1,12 +1,13 @@ getStatus() !== 200) { - throw new DnsException("HTTP result !== 200: ".$response->getReason()); + throw new DoHException("HTTP result !== 200: ".$response->getStatus()." ".$response->getReason(), $response->getStatus()); } $response = yield $response->getBody(); diff --git a/lib/Internal/Socket.php b/lib/Internal/Socket.php index 76dc4df..0dee5b5 100644 --- a/lib/Internal/Socket.php +++ b/lib/Internal/Socket.php @@ -9,6 +9,7 @@ use Amp\ByteStream\StreamException; use Amp\Deferred; use Amp\Dns\DnsException; use Amp\Dns\TimeoutException; +use Amp\DoH\DoHException; use Amp\DoH\Nameserver; use Amp\Promise; use LibDNS\Messages\Message; @@ -142,7 +143,7 @@ abstract class Socket return; } - if (!$exception instanceof DnsException) { + if (!$exception instanceof DnsException && !$exception instanceof DoHException) { $message = "Unexpected error during resolution: ".$exception->getMessage(); $exception = new DnsException($message, 0, $exception); } diff --git a/lib/Rfc8484StubResolver.php b/lib/Rfc8484StubResolver.php index fdbb923..573d5d8 100644 --- a/lib/Rfc8484StubResolver.php +++ b/lib/Rfc8484StubResolver.php @@ -259,7 +259,23 @@ final class Rfc8484StubResolver implements Resolver $attemptDescription[] = $nameserver; /** @var Message $response */ - $response = yield $socket->ask($question, $this->config->getTimeout()); + try { + $response = yield $socket->ask($question, $this->config->getTimeout()); + } catch (DoHException $e) { + // Defer call, because it might interfere with the unreference() call in Internal\Socket otherwise + + $i = ++$attempt % \count($nameservers); + $nameserver = $nameservers[$i]; + $socket = $this->getSocket($nameserver); + continue; + } catch (NoRecordException $e) { + // Defer call, because it might interfere with the unreference() call in Internal\Socket otherwise + + $i = ++$attempt % \count($nameservers); + $nameserver = $nameservers[$i]; + $socket = $this->getSocket($nameserver); + continue; + } $this->assertAcceptableResponse($response); if ($response->isTruncated()) { diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php index 0d3dabf..350b604 100644 --- a/test/IntegrationTest.php +++ b/test/IntegrationTest.php @@ -17,11 +17,13 @@ class IntegrationTest extends TestCase * @group internet * @dataProvider provideServersAndHostnames */ - public function testResolve($hostname, $nameserver) + public function testResolve($hostname, $nameservers) { - $nameserver = new Nameserver(...$nameserver); - Loop::run(function () use ($hostname, $nameserver) { - $DohConfig = new DoH\DoHConfig([$nameserver]); + foreach ($nameservers as &$nameserver) { + $nameserver = new Nameserver(...$nameserver); + } + Loop::run(function () use ($hostname, $nameservers) { + $DohConfig = new DoH\DoHConfig($nameservers); Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); $result = yield Dns\resolve($hostname); @@ -39,11 +41,13 @@ class IntegrationTest extends TestCase * @group internet * @dataProvider provideServersAndHostnames */ - public function testWorksAfterConfigReload($hostname, $nameserver) + public function testWorksAfterConfigReload($hostname, $nameservers) { - $nameserver = new Nameserver(...$nameserver); - Loop::run(function () use ($hostname, $nameserver) { - $DohConfig = new DoH\DoHConfig([$nameserver]); + foreach ($nameservers as &$nameserver) { + $nameserver = new Nameserver(...$nameserver); + } + Loop::run(function () use ($hostname, $nameservers) { + $DohConfig = new DoH\DoHConfig($nameservers); Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); yield Dns\resolve($hostname); @@ -56,11 +60,13 @@ class IntegrationTest extends TestCase * @group internet * @dataProvider provideServers */ - public function testResolveIPv4only($nameserver) + public function testResolveIPv4only($nameservers) { - $nameserver = new Nameserver(...$nameserver); - Loop::run(function () use ($nameserver) { - $DohConfig = new DoH\DoHConfig([$nameserver]); + foreach ($nameservers as &$nameserver) { + $nameserver = new Nameserver(...$nameserver); + } + Loop::run(function () use ($nameservers) { + $DohConfig = new DoH\DoHConfig($nameservers); Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); $records = yield Dns\resolve("google.com", Record::A); @@ -81,11 +87,13 @@ class IntegrationTest extends TestCase * @group internet * @dataProvider provideServers */ - public function testResolveIPv6only($nameserver) + public function testResolveIPv6only($nameservers) { - $nameserver = new Nameserver(...$nameserver); - Loop::run(function () use ($nameserver) { - $DohConfig = new DoH\DoHConfig([$nameserver]); + foreach ($nameservers as &$nameserver) { + $nameserver = new Nameserver(...$nameserver); + } + Loop::run(function () use ($nameservers) { + $DohConfig = new DoH\DoHConfig($nameservers); Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); $records = yield Dns\resolve("google.com", Record::AAAA); @@ -106,11 +114,13 @@ class IntegrationTest extends TestCase * @group internet * @dataProvider provideServers */ - public function testPtrLookup($nameserver) + public function testPtrLookup($nameservers) { - $nameserver = new Nameserver(...$nameserver); - Loop::run(function () use ($nameserver) { - $DohConfig = new DoH\DoHConfig([$nameserver]); + foreach ($nameservers as &$nameserver) { + $nameserver = new Nameserver(...$nameserver); + } + Loop::run(function () use ($nameservers) { + $DohConfig = new DoH\DoHConfig($nameservers); Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); $result = yield Dns\query("8.8.4.4", Record::PTR); @@ -171,12 +181,24 @@ class IntegrationTest extends TestCase public function provideServers() { - return [ - [['https://mozilla.cloudflare-dns.com/dns-query']], - [['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_POST]], - [['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_GET]], - [['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON]], - [['https://google.com/resolve', Nameserver::GOOGLE_JSON, ["Host" => "dns.google.com"]]], + $nameservers = [ + ['https://mozilla.cloudflare-dns.com/dns-query'], + ['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_POST], + ['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_GET], + ['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON], + ['https://google.com/resolve', Nameserver::GOOGLE_JSON, ["Host" => "dns.google.com"]], ]; + $result = []; + for ($start = 0; $start < count($nameservers); $start++) { + $temp = []; + for ($i = 0; $i < count($nameservers); $i++) { + $i = ($start + $i) % count($nameservers); + + $temp[] = $nameservers[$i]; + } + $result[] = [$temp]; + } + + return $result; } } diff --git a/test/Rfc8484StubResolverTest.php b/test/Rfc8484StubResolverTest.php index b9ba363..4cc34f0 100644 --- a/test/Rfc8484StubResolverTest.php +++ b/test/Rfc8484StubResolverTest.php @@ -66,4 +66,21 @@ class Rfc8484StubResolverTest extends TestCase new Rfc8484StubResolver($DohConfig); }); } + + public function testInvalidNameserverFallback() + { + Loop::run(function () { + $DohConfig = new DoH\DoHConfig( + [ + new DoH\Nameserver('https://google.com/wrong-uri'), + new DoH\Nameserver('https://google.com/wrong-uri'), + new DoH\Nameserver('https://nonexistant-dns.com/dns-query'), + new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query'), + ] + ); + $resolver = new Rfc8484StubResolver($DohConfig); + $this->assertInstanceOf(Rfc8484StubResolver::class, $resolver); + yield $resolver->resolve('google.com'); + }); + } }