Adapt for new amphp/dns

This commit is contained in:
Daniil Gentili 2023-01-12 15:49:35 +01:00
parent 1f6935b973
commit b98f463294
15 changed files with 226 additions and 188 deletions

View File

@ -21,24 +21,34 @@ require __DIR__ . '/examples/_bootstrap.php';
use Amp\DoH; use Amp\DoH;
use Amp\Dns; use Amp\Dns;
use Amp\Loop; use Amp\Dns\DnsRecord;
use function Amp\Future\awaitFirst;
// Set default resolver to DNS-over-HTTPS resolver // Set default resolver to DNS-over-HTTPS resolver
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]); // Defaults to DoH\NameserverType::RFC8484_POST $DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]); // Defaults to DoH\NameserverType::RFC8484_POST
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); Dns\dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$githubIpv4 = Dns\resolve("github.com", Dns\Record::A); $githubIpv4 = Dns\resolve("github.com", DnsRecord::A);
pretty_print_records("github.com", $githubIpv4); pretty_print_records("github.com", $githubIpv4);
$googleIpv4 = \Amp\async(fn () => Amp\Dns\resolve("google.com", Dns\Record::A)); $googleIpv4 = \Amp\async(fn () => Amp\Dns\resolve("google.com", DnsRecord::A));
$googleIpv6 = \Amp\async(fn () => Amp\Dns\resolve("google.com", Dns\Record::AAAA)); $googleIpv6 = \Amp\async(fn () => Amp\Dns\resolve("google.com", DnsRecord::AAAA));
$firstGoogleResult = Amp\awaitAll([$googleIpv4, $googleIpv6]); $firstGoogleResult = awaitFirst([$googleIpv4, $googleIpv6]);
pretty_print_records("google.com", $firstGoogleResult); pretty_print_records("google.com", $firstGoogleResult);
$combinedGoogleResult = Amp\Dns\resolve("google.com"); $combinedGoogleResult = Amp\Dns\resolve("google.com");
pretty_print_records("google.com", $combinedGoogleResult); pretty_print_records("google.com", $combinedGoogleResult);
$googleMx = Amp\Dns\query("google.com", Amp\Dns\Record::MX); $googleMx = Amp\Dns\query("google.com", DnsRecord::MX);
pretty_print_records("google.com", $googleMx);
$firstGoogleResult = awaitFirst([$googleIpv4, $googleIpv6]);
pretty_print_records("google.com", $firstGoogleResult);
$combinedGoogleResult = Amp\Dns\resolve("google.com");
pretty_print_records("google.com", $combinedGoogleResult);
$googleMx = Amp\Dns\query("google.com", DnsRecord::MX);
pretty_print_records("google.com", $googleMx); pretty_print_records("google.com", $googleMx);
``` ```

View File

@ -40,24 +40,25 @@
"email": "aaron@trowski.com" "email": "aaron@trowski.com"
} }
], ],
"minimum-stablity": "dev",
"require": { "require": {
"php": ">=8.1", "php": ">=8.1",
"amphp/cache": "^v2-dev", "amphp/cache": "^2",
"amphp/parser": "^1", "amphp/parser": "^1",
"danog/libdns-json": "^0.2", "danog/libdns-json": "^0.2",
"daverandom/libdns": "^2.0.1", "daverandom/libdns": "^2.0.1",
"amphp/amp": "^v3-dev", "amphp/amp": "^3",
"amphp/http-client": "^v5-dev", "amphp/http-client": "v5.x-dev",
"amphp/dns": "^v2-dev", "amphp/socket": "v2.x-dev",
"amphp/dns": "^2",
"ext-filter": "*", "ext-filter": "*",
"ext-json": "*" "ext-json": "*"
}, },
"minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,
"require-dev": { "require-dev": {
"amphp/phpunit-util": "^3", "amphp/phpunit-util": "^3",
"phpunit/phpunit": "^9", "phpunit/phpunit": "^9",
"amphp/php-cs-fixer-config": "^2-dev", "amphp/php-cs-fixer-config": "^2",
"psalm/phar": "^5" "psalm/phar": "^5"
}, },
"autoload": { "autoload": {

View File

@ -39,7 +39,7 @@ $nameservers []= new DoH\Nameserver('https://google.com/resolve', DoH\Nameserver
$DohConfig = new DoH\DoHConfig($nameservers); $DohConfig = new DoH\DoHConfig($nameservers);
// Set default resolver for all AMPHP apps to DNS-over-HTTPS resolver // Set default resolver for all AMPHP apps to DNS-over-HTTPS resolver
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); Dns\dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
``` ```
In the last example, [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting), useful to bypass censorship in non-free countries: from the outside, it looks like the DoH client is connecting to `https://google.com`, but by sending a custom Host HTTP header to the server after the TLS handshake is finished, the server that actually replies is `https://dns.google.com` (this is only possible if both servers are behind a common CDN that allows domain fronting, like google's CDN). In the last example, [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting), useful to bypass censorship in non-free countries: from the outside, it looks like the DoH client is connecting to `https://google.com`, but by sending a custom Host HTTP header to the server after the TLS handshake is finished, the server that actually replies is `https://dns.google.com` (this is only possible if both servers are behind a common CDN that allows domain fronting, like google's CDN).
@ -47,7 +47,7 @@ In normal conditions, it is recommended that you use mozilla+cloudflare's DoH en
Other parameters that can be passed to the DoHConfig constructor are: Other parameters that can be passed to the DoHConfig constructor are:
```php ```php
public function __construct(array $nameservers, \Amp\Artax\Client $artax = null, \Amp\Dns\Resolver $resolver = null, \Amp\Dns\ConfigLoader $configLoader = null, \Amp\Cache\Cache $cache = null); public function __construct(array $nameservers, \Amp\Artax\Client $artax = null, \Amp\Dns\dnsResolver $resolver = null, \Amp\Dns\ConfigLoader $configLoader = null, \Amp\Cache\Cache $cache = null);
``` ```
You can provide a custom HTTP client to use for resolution, or use a custom subresolver (the subresolver is used to make the first and only plaintext DNS request to obtain the address of the DoH nameserver), or use a [custom configuration](https://amphp.org/dns/#configuration) for the DoH client (and the subresolver, too, if the configuration is provided but the resolver isn't). You can provide a custom HTTP client to use for resolution, or use a custom subresolver (the subresolver is used to make the first and only plaintext DNS request to obtain the address of the DoH nameserver), or use a [custom configuration](https://amphp.org/dns/#configuration) for the DoH client (and the subresolver, too, if the configuration is provided but the resolver isn't).
@ -55,7 +55,7 @@ The last parameter can be a custom async caching object.
### Address Resolution ### Address Resolution
To resolve addresses using `dns-over-https` first set the global DNS resolver as explained in the [configuration section](#configuration), or use an instance of `Rfc8484StubResolver` instead of `Rfc1035StubResolver`. To resolve addresses using `dns-over-https` first set the global DNS resolver as explained in the [configuration section](#configuration), or use an instance of `Rfc8484StubDohResolver` instead of `Rfc1035StubResolver`.
`Amp\Dns\resolve` provides hostname to IP address resolution. It returns an array of IPv4 and IPv6 addresses by default. The type of IP addresses returned can be restricted by passing a second argument with the respective type. `Amp\Dns\resolve` provides hostname to IP address resolution. It returns an array of IPv4 and IPv6 addresses by default. The type of IP addresses returned can be restricted by passing a second argument with the respective type.
@ -76,7 +76,7 @@ $records = Amp\Dns\resolve("github.com", Amp\Dns\Record::A);
### Custom Queries ### Custom Queries
To resolve addresses using `dns-over-https` first set the global DNS resolver as explained in the [configuration section](#configuration), or use an instance of `Rfc8484StubResolver` instead of `Rfc1035StubResolver`. To resolve addresses using `dns-over-https` first set the global DNS resolver as explained in the [configuration section](#configuration), or use an instance of `Rfc8484StubDohResolver` instead of `Rfc1035StubResolver`.
`Amp\Dns\query` supports the various other DNS record types such as `MX`, `PTR`, or `TXT`. It automatically rewrites passed IP addresses for `PTR` lookups. `Amp\Dns\query` supports the various other DNS record types such as `MX`, `PTR`, or `TXT`. It automatically rewrites passed IP addresses for `PTR` lookups.
@ -92,7 +92,7 @@ $records = Amp\Dns\query("8.8.8.8", Amp\Dns\Record::PTR);
### Caching ### Caching
The `Rfc8484StubResolver` caches responses by default in an `Amp\Cache\LocalCache`. You can set any other `Amp\Cache\Cache` implementation by creating a custom instance of `Rfc8484StubResolver` and setting that via `Amp\Dns\resolver()`, but it's usually unnecessary. If you have a lot of very short running scripts, you might want to consider using a local DNS resolver with a cache instead of setting a custom cache implementation, such as `dnsmasq`. The `Rfc8484StubDohResolver` caches responses by default in an `Amp\Cache\LocalCache`. You can set any other `Amp\Cache\Cache` implementation by creating a custom instance of `Rfc8484StubDohResolver` and setting that via `Amp\Dns\dnsResolver()`, but it's usually unnecessary. If you have a lot of very short running scripts, you might want to consider using a local DNS resolver with a cache instead of setting a custom cache implementation, such as `dnsmasq`.
### Reloading Configuration ### Reloading Configuration
@ -100,7 +100,7 @@ The subresolver (which is the resolver set in the `DoHConfig`, `Rfc1035StubResol
```php ```php
Loop::repeat(60000, function () use ($resolver) { Loop::repeat(60000, function () use ($resolver) {
Amp\Dns\resolver()->reloadConfig(); Amp\Dns\dnsResolver()->reloadConfig();
}); });
``` ```

View File

@ -1,21 +1,24 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use Amp\Dns\Record; use Amp\Dns\DnsRecord;
require __DIR__ . "/../vendor/autoload.php"; require __DIR__ . "/../vendor/autoload.php";
function pretty_print_records(string $queryName, array $records) /**
* @param array<DnsRecord> $records
*/
function pretty_print_records(string $queryName, array $records): void
{ {
print "---------- " . $queryName . " " . str_repeat("-", 55 - strlen($queryName)) . " TTL --\r\n"; print "---------- " . $queryName . " " . str_repeat("-", 55 - strlen($queryName)) . " TTL --\r\n";
$format = "%-10s %-56s %-5d\r\n"; $format = "%-10s %-56s %-5d\r\n";
foreach ($records as $record) { foreach ($records as $record) {
print sprintf($format, Record::getName($record->getType()), $record->getValue(), $record->getTtl()); print sprintf($format, DnsRecord::getName($record->getType()), $record->getValue(), $record->getTtl());
} }
} }
function pretty_print_error(string $queryName, \Throwable $error) function pretty_print_error(string $queryName, \Throwable $error): void
{ {
print "-- " . $queryName . " " . str_repeat("-", 70 - strlen($queryName)) . "\r\n"; print "-- " . $queryName . " " . str_repeat("-", 70 - strlen($queryName)) . "\r\n";
print get_class($error) . ": " . $error->getMessage() . "\r\n"; print get_class($error) . ": " . $error->getMessage() . "\r\n";

View File

@ -16,14 +16,14 @@ $domains = array_map(function ($line) {
array_shift($domains); array_shift($domains);
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]); $DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); Dns\dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
print "Starting sequential queries...\r\n\r\n"; print "Starting sequential queries...\r\n\r\n";
$timings = []; $timings = [];
for ($i = 0; $i < 10; $i++) { for ($i = 0; $i < 10; $i++) {
$start = microtime(1); $start = microtime(true);
$domain = $domains[random_int(0, count($domains) - 1)]; $domain = $domains[random_int(0, count($domains) - 1)];
try { try {
@ -32,7 +32,7 @@ for ($i = 0; $i < 10; $i++) {
pretty_print_error($domain, $e); pretty_print_error($domain, $e);
} }
$time = round(microtime(1) - $start, 2); $time = round(microtime(true) - $start, 2);
$timings[] = $time; $timings[] = $time;
printf("%'-74s\r\n\r\n", " in " . $time . " ms"); printf("%'-74s\r\n\r\n", " in " . $time . " ms");

View File

@ -13,7 +13,7 @@ $customConfigLoader = new class implements Dns\DnsConfigLoader {
return new Dns\DnsConfig([ return new Dns\DnsConfig([
"8.8.8.8:53", "8.8.8.8:53",
"[2001:4860:4860::8888]:53", "[2001:4860:4860::8888]:53",
], $hosts, 5, 3); ], $hosts);
} }
}; };
@ -28,7 +28,7 @@ $DohConfig = new DoH\DoHConfig(
null, null,
$customConfigLoader $customConfigLoader
); );
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); Dns\dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$hostname = $argv[1] ?? "amphp.org"; $hostname = $argv[1] ?? "amphp.org";

View File

@ -3,16 +3,17 @@
require __DIR__ . "/_bootstrap.php"; require __DIR__ . "/_bootstrap.php";
use Amp\Dns; use Amp\Dns;
use Amp\Dns\DnsRecord;
use Amp\DoH; use Amp\DoH;
// Set default resolver to DNS-over-HTTPS resolver // Set default resolver to DNS-over-HTTPS resolver
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]); $DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); Dns\dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$ip = $argv[1] ?? "8.8.8.8"; $ip = $argv[1] ?? "8.8.8.8";
try { try {
pretty_print_records($ip, Dns\query($ip, Dns\Record::PTR)); pretty_print_records($ip, Dns\query($ip, DnsRecord::PTR));
} catch (Dns\DnsException $e) { } catch (Dns\DnsException $e) {
pretty_print_error($ip, $e); pretty_print_error($ip, $e);
} }

View File

@ -4,9 +4,10 @@ namespace Amp\DoH;
use Amp\Cache\Cache; use Amp\Cache\Cache;
use Amp\Cache\LocalCache; use Amp\Cache\LocalCache;
use Amp\Dns\ConfigException; use Amp\Dns\DnsConfigException;
use Amp\Dns\DnsConfigLoader; use Amp\Dns\DnsConfigLoader;
use Amp\Dns\Rfc1035StubResolver; use Amp\Dns\DnsResolver;
use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Dns\UnixDnsConfigLoader; use Amp\Dns\UnixDnsConfigLoader;
use Amp\Dns\WindowsDnsConfigLoader; use Amp\Dns\WindowsDnsConfigLoader;
use Amp\Http\Client\DelegateHttpClient; use Amp\Http\Client\DelegateHttpClient;
@ -19,24 +20,24 @@ final class DoHConfig
*/ */
private readonly array $nameservers; private readonly array $nameservers;
private readonly DelegateHttpClient $httpClient; private readonly DelegateHttpClient $httpClient;
private readonly Rfc1035StubResolver $subResolver; private readonly DnsResolver $subResolver;
private readonly DnsConfigLoader $configLoader; private readonly DnsConfigLoader $configLoader;
private readonly Cache $cache; private readonly Cache $cache;
/** /**
* @param non-empty-array<Nameserver> $nameservers * @param non-empty-array<Nameserver> $nameservers
*/ */
public function __construct(array $nameservers, ?DelegateHttpClient $httpClient = null, ?Rfc1035StubResolver $resolver = null, ?DnsConfigLoader $configLoader = null, ?Cache $cache = null) public function __construct(array $nameservers, ?DelegateHttpClient $httpClient = null, ?DnsResolver $resolver = null, ?DnsConfigLoader $configLoader = null, ?Cache $cache = null)
{ {
/** @psalm-suppress TypeDoesNotContainType */ /** @psalm-suppress TypeDoesNotContainType */
if (\count($nameservers) < 1) { if (\count($nameservers) < 1) {
throw new ConfigException("At least one nameserver is required for a valid config"); throw new DnsConfigException("At least one nameserver is required for a valid config");
} }
foreach ($nameservers as $nameserver) { foreach ($nameservers as $nameserver) {
/** @psalm-suppress DocblockContradiction */ /** @psalm-suppress DocblockContradiction */
if (!($nameserver instanceof Nameserver)) { if (!($nameserver instanceof Nameserver)) {
throw new ConfigException("Invalid nameserver: {$nameserver}"); throw new DnsConfigException("Invalid nameserver: {$nameserver}");
} }
} }
@ -46,7 +47,7 @@ final class DoHConfig
$this->configLoader = $configLoader ?? (\stripos(PHP_OS, "win") === 0 $this->configLoader = $configLoader ?? (\stripos(PHP_OS, "win") === 0
? new WindowsDnsConfigLoader() ? new WindowsDnsConfigLoader()
: new UnixDnsConfigLoader()); : new UnixDnsConfigLoader());
$this->subResolver = $resolver ?? new Rfc1035StubResolver(null, $this->configLoader); $this->subResolver = $resolver ?? new Rfc1035StubDnsResolver(null, $this->configLoader);
} }
/** /**
@ -80,7 +81,7 @@ final class DoHConfig
{ {
return $this->configLoader; return $this->configLoader;
} }
public function getSubResolver(): Rfc1035StubResolver public function getSubResolver(): DnsResolver
{ {
return $this->subResolver; return $this->subResolver;
} }

View File

@ -2,7 +2,7 @@
namespace Amp\DoH; namespace Amp\DoH;
use Amp\Dns\ConfigException; use Amp\Dns\DnsConfigException;
final class Nameserver final class Nameserver
{ {
@ -18,7 +18,7 @@ final class Nameserver
private readonly array $headers = [] private readonly array $headers = []
) { ) {
if (\parse_url($uri, PHP_URL_SCHEME) !== 'https') { if (\parse_url($uri, PHP_URL_SCHEME) !== 'https') {
throw new ConfigException('Did not provide a valid HTTPS url!'); throw new DnsConfigException('Did not provide a valid HTTPS url!');
} }
$this->host = \parse_url($uri, PHP_URL_HOST); $this->host = \parse_url($uri, PHP_URL_HOST);
} }

View File

@ -5,15 +5,15 @@ namespace Amp\DoH;
use Amp\Cache\Cache; use Amp\Cache\Cache;
use Amp\Cancellation; use Amp\Cancellation;
use Amp\CompositeException; use Amp\CompositeException;
use Amp\Dns\ConfigException;
use Amp\Dns\DnsConfig; use Amp\Dns\DnsConfig;
use Amp\Dns\DnsConfigException;
use Amp\Dns\DnsConfigLoader; use Amp\Dns\DnsConfigLoader;
use Amp\Dns\DnsException; use Amp\Dns\DnsException;
use Amp\Dns\NoRecordException; use Amp\Dns\DnsRecord;
use Amp\Dns\Record; use Amp\Dns\DnsResolver;
use Amp\Dns\Resolver; use Amp\Dns\DnsTimeoutException;
use Amp\Dns\Rfc1035StubResolver; use Amp\Dns\MissingDnsRecordException;
use Amp\Dns\TimeoutException; use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Future; use Amp\Future;
use Amp\Http\Client\DelegateHttpClient; use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\Request; use Amp\Http\Client\Request;
@ -35,7 +35,7 @@ use LibDNS\Records\QuestionFactory;
use function Amp\async; use function Amp\async;
use function Amp\Dns\normalizeName; use function Amp\Dns\normalizeName;
final class Rfc8484StubResolver implements Resolver final class Rfc8484StubDohResolver implements DnsResolver
{ {
const CACHE_PREFIX = "amphp.doh."; const CACHE_PREFIX = "amphp.doh.";
@ -50,7 +50,7 @@ final class Rfc8484StubResolver implements Resolver
/** @var Future[] */ /** @var Future[] */
private array $pendingQueries = []; private array $pendingQueries = [];
private Rfc1035StubResolver $subResolver; private DnsResolver $subResolver;
private Encoder $encoder; private Encoder $encoder;
private Decoder $decoder; private Decoder $decoder;
private QueryEncoder $encoderJson; private QueryEncoder $encoderJson;
@ -75,31 +75,31 @@ final class Rfc8484StubResolver implements Resolver
/** @inheritdoc */ /** @inheritdoc */
public function resolve(string $name, int $typeRestriction = null, ?Cancellation $cancellation = null): array public function resolve(string $name, int $typeRestriction = null, ?Cancellation $cancellation = null): array
{ {
if ($typeRestriction !== null && $typeRestriction !== Record::A && $typeRestriction !== Record::AAAA) { if ($typeRestriction !== null && $typeRestriction !== DnsRecord::A && $typeRestriction !== DnsRecord::AAAA) {
throw new \Error("Invalid value for parameter 2: null|Record::A|Record::AAAA expected"); throw new \Error("Invalid value for parameter 2: null|DnsRecord::A|DnsRecord::AAAA expected");
} }
if (!$this->config) { if (!$this->config) {
try { try {
$this->reloadConfig(); $this->reloadConfig();
} catch (ConfigException $e) { } catch (DnsConfigException $e) {
$this->config = new DnsConfig(['0.0.0.0'], []); $this->config = new DnsConfig(['0.0.0.0'], []);
} }
} }
switch ($typeRestriction) { switch ($typeRestriction) {
case Record::A: case DnsRecord::A:
if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return [new Record($name, Record::A, null)]; return [new DnsRecord($name, DnsRecord::A, null)];
} }
if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
throw new DnsException("Got an IPv6 address, but type is restricted to IPv4"); throw new DnsException("Got an IPv6 address, but type is restricted to IPv4");
} }
break; break;
case Record::AAAA: case DnsRecord::AAAA:
if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return [new Record($name, Record::AAAA, null)]; return [new DnsRecord($name, DnsRecord::AAAA, null)];
} }
if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
@ -108,11 +108,11 @@ final class Rfc8484StubResolver implements Resolver
break; break;
default: default:
if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return [new Record($name, Record::A, null)]; return [new DnsRecord($name, DnsRecord::A, null)];
} }
if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return [new Record($name, Record::AAAA, null)]; return [new DnsRecord($name, DnsRecord::AAAA, null)];
} }
break; break;
} }
@ -128,9 +128,9 @@ final class Rfc8484StubResolver implements Resolver
// Follow RFC 6761 and never send queries for localhost to the caching DNS server // Follow RFC 6761 and never send queries for localhost to the caching DNS server
// Usually, these queries are already resolved via queryHosts() // Usually, these queries are already resolved via queryHosts()
if ($name === 'localhost') { if ($name === 'localhost') {
return $typeRestriction === Record::AAAA return $typeRestriction === DnsRecord::AAAA
? [new Record('::1', Record::AAAA, null)] ? [new DnsRecord('::1', DnsRecord::AAAA, null)]
: [new Record('127.0.0.1', Record::A, null)]; : [new DnsRecord('127.0.0.1', DnsRecord::A, null)];
} }
if ($this->dohConfig->isNameserver($name)) { if ($this->dohConfig->isNameserver($name)) {
@ -157,15 +157,15 @@ final class Rfc8484StubResolver implements Resolver
} }
[$exceptions, $records] = Future\awaitAll([ [$exceptions, $records] = Future\awaitAll([
async(fn () => $this->query($searchName, Record::A, $cancellation)), async(fn () => $this->query($searchName, DnsRecord::A, $cancellation)),
async(fn () => $this->query($searchName, Record::AAAA, $cancellation)), async(fn () => $this->query($searchName, DnsRecord::AAAA, $cancellation)),
]); ]);
if (\count($exceptions) === 2) { if (\count($exceptions) === 2) {
$errors = []; $errors = [];
foreach ($exceptions as $reason) { foreach ($exceptions as $reason) {
if ($reason instanceof NoRecordException) { if ($reason instanceof MissingDnsRecordException) {
throw $reason; throw $reason;
} }
@ -184,13 +184,13 @@ final class Rfc8484StubResolver implements Resolver
} }
return \array_merge(...$records); return \array_merge(...$records);
} catch (NoRecordException) { } catch (MissingDnsRecordException) {
try { try {
$cnameRecords = $this->query($searchName, Record::CNAME, $cancellation); $cnameRecords = $this->query($searchName, DnsRecord::CNAME, $cancellation);
$name = $cnameRecords[0]->getValue(); $name = $cnameRecords[0]->getValue();
continue; continue;
} catch (NoRecordException) { } catch (MissingDnsRecordException) {
$dnameRecords = $this->query($searchName, Record::DNAME, $cancellation); $dnameRecords = $this->query($searchName, DnsRecord::DNAME, $cancellation);
$name = $dnameRecords[0]->getValue(); $name = $dnameRecords[0]->getValue();
continue; continue;
} }
@ -219,7 +219,9 @@ final class Rfc8484StubResolver implements Resolver
if (!$this->pendingConfig) { if (!$this->pendingConfig) {
$promise = async(function () { $promise = async(function () {
try { try {
if ($this->subResolver instanceof Rfc1035StubDnsResolver) {
$this->subResolver->reloadConfig(); $this->subResolver->reloadConfig();
}
$this->config = $this->configLoader->loadConfig(); $this->config = $this->configLoader->loadConfig();
} finally { } finally {
$this->pendingConfig = null; $this->pendingConfig = null;
@ -237,15 +239,15 @@ final class Rfc8484StubResolver implements Resolver
$hosts = $this->config->getKnownHosts(); $hosts = $this->config->getKnownHosts();
$records = []; $records = [];
$returnIPv4 = $typeRestriction === null || $typeRestriction === Record::A; $returnIPv4 = $typeRestriction === null || $typeRestriction === DnsRecord::A;
$returnIPv6 = $typeRestriction === null || $typeRestriction === Record::AAAA; $returnIPv6 = $typeRestriction === null || $typeRestriction === DnsRecord::AAAA;
if ($returnIPv4 && isset($hosts[Record::A][$name])) { if ($returnIPv4 && isset($hosts[DnsRecord::A][$name])) {
$records[] = new Record($hosts[Record::A][$name], Record::A, null); $records[] = new DnsRecord($hosts[DnsRecord::A][$name], DnsRecord::A, null);
} }
if ($returnIPv6 && isset($hosts[Record::AAAA][$name])) { if ($returnIPv6 && isset($hosts[DnsRecord::AAAA][$name])) {
$records[] = new Record($hosts[Record::AAAA][$name], Record::AAAA, null); $records[] = new DnsRecord($hosts[DnsRecord::AAAA][$name], DnsRecord::AAAA, null);
} }
return $records; return $records;
@ -266,7 +268,7 @@ final class Rfc8484StubResolver implements Resolver
if (!$this->config) { if (!$this->config) {
try { try {
$this->reloadConfig(); $this->reloadConfig();
} catch (ConfigException $e) { } catch (DnsConfigException $e) {
$this->config = new DnsConfig(['0.0.0.0'], []); $this->config = new DnsConfig(['0.0.0.0'], []);
} }
} }
@ -296,7 +298,7 @@ final class Rfc8484StubResolver implements Resolver
$this->assertAcceptableResponse($response); $this->assertAcceptableResponse($response);
if ($response->isTruncated()) { if ($response->isTruncated()) {
throw new DnsException("Server returned a truncated response for '{$name}' (".Record::getName($type).")"); throw new DnsException("Server returned a truncated response for '{$name}' (".DnsRecord::getName($type).")");
} }
$answers = $response->getAnswerRecords(); $answers = $response->getAnswerRecords();
@ -319,22 +321,22 @@ final class Rfc8484StubResolver implements Resolver
if (!isset($result[$type])) { if (!isset($result[$type])) {
// "it MUST NOT cache it for longer than five (5) minutes" per RFC 2308 section 7.1 // "it MUST NOT cache it for longer than five (5) minutes" per RFC 2308 section 7.1
$this->cache->set($this->getCacheKey($name, $type), \json_encode([]), 300); $this->cache->set($this->getCacheKey($name, $type), \json_encode([]), 300);
throw new NoRecordException("No records returned for '{$name}' (".Record::getName($type).")"); throw new MissingDnsRecordException("No records returned for '{$name}' (".DnsRecord::getName($type).")");
} }
return \array_map(function ($data) use ($type, $ttls) { return \array_map(function ($data) use ($type, $ttls) {
return new Record($data, $type, $ttls[$type]); return new DnsRecord($data, $type, $ttls[$type]);
}, $result[$type]); }, $result[$type]);
} catch (TimeoutException) { } catch (DnsTimeoutException) {
$i = ++$attempt % \count($nameservers); $i = ++$attempt % \count($nameservers);
$nameserver = $nameservers[$i]; $nameserver = $nameservers[$i];
} }
} }
throw new TimeoutException(\sprintf( throw new DnsTimeoutException(\sprintf(
"No response for '%s' (%s) from any nameserver within %d ms after %d attempts, tried %s", "No response for '%s' (%s) from any nameserver within %d ms after %d attempts, tried %s",
$name, $name,
Record::getName($type), DnsRecord::getName($type),
$this->config->getTimeout(), $this->config->getTimeout(),
$attempts, $attempts,
\implode(", ", $attemptDescription) \implode(", ", $attemptDescription)
@ -395,7 +397,7 @@ final class Rfc8484StubResolver implements Resolver
private function normalizeName(string $name, int $type): string private function normalizeName(string $name, int $type): string
{ {
if ($type === Record::PTR) { if ($type === DnsRecord::PTR) {
if (($packedIp = @\inet_pton($name)) !== false) { if (($packedIp = @\inet_pton($name)) !== false) {
if (isset($packedIp[4])) { // IPv6 if (isset($packedIp[4])) { // IPv6
$name = \wordwrap(\strrev(\bin2hex($packedIp)), 1, ".", true).".ip6.arpa"; $name = \wordwrap(\strrev(\bin2hex($packedIp)), 1, ".", true).".ip6.arpa";
@ -403,7 +405,7 @@ final class Rfc8484StubResolver implements Resolver
$name = \inet_ntop(\strrev($packedIp)).".in-addr.arpa"; $name = \inet_ntop(\strrev($packedIp)).".in-addr.arpa";
} }
} }
} elseif (\in_array($type, [Record::A, Record::AAAA])) { } elseif (\in_array($type, [DnsRecord::A, DnsRecord::AAAA])) {
$name = normalizeName($name); $name = normalizeName($name);
} }
@ -438,20 +440,20 @@ final class Rfc8484StubResolver implements Resolver
} }
/** /**
* @return list<Record> * @return list<DnsRecord>
*/ */
private function decodeCachedResult(string $name, int $type, string $encoded): array private function decodeCachedResult(string $name, int $type, string $encoded): array
{ {
$decoded = \json_decode($encoded, true); $decoded = \json_decode($encoded, true);
if (!$decoded) { if (!$decoded) {
throw new NoRecordException("No records returned for {$name} (cached result)"); throw new MissingDnsRecordException("No records returned for {$name} (cached result)");
} }
$result = []; $result = [];
foreach ($decoded as $data) { foreach ($decoded as $data) {
$result[] = new Record($data, $type); $result[] = new DnsRecord($data, $type);
} }
return $result; return $result;

View File

@ -8,6 +8,8 @@
> >
<projectFiles> <projectFiles>
<directory name="lib" /> <directory name="lib" />
<directory name="test" />
<directory name="examples" />
<ignoreFiles> <ignoreFiles>
<directory name="vendor" /> <directory name="vendor" />
</ignoreFiles> </ignoreFiles>

View File

@ -2,30 +2,38 @@
namespace Amp\DoH\Test; namespace Amp\DoH\Test;
use Amp\Cache\Cache;
use Amp\Cache\LocalCache; use Amp\Cache\LocalCache;
use Amp\Dns\ConfigException; use Amp\Dns\DnsConfigException;
use Amp\Dns\Rfc1035StubResolver; use Amp\Dns\DnsConfigLoader;
use Amp\Dns\DnsResolver;
use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Dns\UnixDnsConfigLoader; use Amp\Dns\UnixDnsConfigLoader;
use Amp\Dns\WindowsDnsConfigLoader; use Amp\Dns\WindowsDnsConfigLoader;
use Amp\DoH\DoHConfig; use Amp\DoH\DoHConfig;
use Amp\DoH\Nameserver; use Amp\DoH\Nameserver;
use Amp\DoH\NameserverType; use Amp\DoH\NameserverType;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder; use Amp\Http\Client\HttpClientBuilder;
use Amp\PHPUnit\AsyncTestCase; use Amp\PHPUnit\AsyncTestCase;
/** @psalm-suppress PropertyNotSetInConstructor */
class DoHConfigTest extends AsyncTestCase class DoHConfigTest extends AsyncTestCase
{ {
/** /**
* @param string[] $nameservers Valid server array. * @param non-empty-list<NameServer> $nameservers Valid server array.
* *
* @dataProvider provideValidServers * @dataProvider provideValidServers
*/ */
public function testAcceptsValidServers(array $nameservers) public function testAcceptsValidServers(array $nameservers): void
{ {
$this->assertInstanceOf(DoHConfig::class, new DoHConfig($nameservers)); $this->assertInstanceOf(DoHConfig::class, new DoHConfig($nameservers));
} }
public function provideValidServers() /**
* @return list<non-empty-list<list{0: Nameserver, 1?: NameserverType}>>
*/
public function provideValidServers(): array
{ {
return [ return [
[[new Nameserver('https://cloudflare-dns.com/dns-query')]], [[new Nameserver('https://cloudflare-dns.com/dns-query')]],
@ -38,16 +46,19 @@ class DoHConfigTest extends AsyncTestCase
} }
/** /**
* @param string[] $nameservers Invalid server array. * @param list<mixed> $nameservers Invalid server array.
* *
* @dataProvider provideInvalidServers * @dataProvider provideInvalidServers
*/ */
public function testRejectsInvalidServers(array $nameservers) public function testRejectsInvalidServers(array $nameservers): void
{ {
$this->expectException(ConfigException::class); $this->expectException(DnsConfigException::class);
new DoHConfig($nameservers); new DoHConfig($nameservers);
} }
/**
* @return list<list{mixed}>
*/
public function provideInvalidServers() public function provideInvalidServers()
{ {
return [ return [
@ -75,47 +86,50 @@ class DoHConfigTest extends AsyncTestCase
} }
/** /**
* @param \Amp\Http\Client\DelegateHttpClient $client Valid HttpClient instance
*
* @dataProvider provideValidHttpClient * @dataProvider provideValidHttpClient
*/ */
public function testAcceptsValidHttpClient($client) public function testAcceptsValidHttpClient(HttpClient $client): void
{ {
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], $client)); $this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], $client));
} }
public function provideValidHttpClient() /**
* @return list<list{HttpClient}>
*/
public function provideValidHttpClient(): array
{ {
return [ return [
[HttpClientBuilder::buildDefault()], [HttpClientBuilder::buildDefault()],
]; ];
} }
/** /**
* @param \Amp\Dns\Resolver $resolver Valid resolver instance
*
* @dataProvider provideValidResolver * @dataProvider provideValidResolver
*/ */
public function testAcceptsValidResolver($resolver) public function testAcceptsValidResolver(DnsResolver $resolver): void
{ {
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, $resolver)); $this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, $resolver));
} }
/**
* @return list<list{DnsResolver}>
*/
public function provideValidResolver() public function provideValidResolver()
{ {
return [ return [
[new Rfc1035StubResolver()], [new Rfc1035StubDnsResolver()],
]; ];
} }
/** /**
* @param $configLoader \Amp\Dns\ConfigLoader Valid ConfigLoader instance
*
* @dataProvider provideValidConfigLoader * @dataProvider provideValidConfigLoader
*/ */
public function testAcceptsValidConfigLoader($configLoader) public function testAcceptsValidConfigLoader(DnsConfigLoader $configLoader): void
{ {
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, null, $configLoader)); $this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, null, $configLoader));
} }
/**
* @return list<list{DnsConfigLoader}>
*/
public function provideValidConfigLoader() public function provideValidConfigLoader()
{ {
return [ return [
@ -125,15 +139,16 @@ class DoHConfigTest extends AsyncTestCase
]; ];
} }
/** /**
* @param \Amp\Cache\Cache Valid cache instance
*
* @dataProvider provideValidCache * @dataProvider provideValidCache
*/ */
public function testAcceptsValidCache($cache) public function testAcceptsValidCache(Cache $cache): void
{ {
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, null, null, $cache)); $this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, null, null, $cache));
} }
/**
* @return list<list{Cache}>
*/
public function provideValidCache() public function provideValidCache()
{ {
return [ return [

View File

@ -3,31 +3,31 @@
namespace Amp\DoH\Test; namespace Amp\DoH\Test;
use Amp\Dns; use Amp\Dns;
use Amp\Dns\Record; use Amp\Dns\DnsRecord;
use Amp\DoH; use Amp\DoH;
use Amp\DoH\Nameserver; use Amp\DoH\Nameserver;
use Amp\DoH\NameserverType; use Amp\DoH\NameserverType;
use Amp\PHPUnit\AsyncTestCase; use Amp\PHPUnit\AsyncTestCase;
use function Amp\delay; use function Amp\delay;
use function Amp\Dns\dnsResolver;
/** @psalm-suppress PropertyNotSetInConstructor */
class IntegrationTest extends AsyncTestCase class IntegrationTest extends AsyncTestCase
{ {
/** /**
* @param string $hostname * @param non-empty-list<list{0: string, 1?: NameserverType}> $nameservers
* @group internet * @group internet
* @dataProvider provideServersAndHostnames * @dataProvider provideServersAndHostnames
*/ */
public function testResolve($hostname, $nameservers) public function testResolve(string $hostname, array $nameservers): void
{ {
foreach ($nameservers as &$nameserver) { $nameservers = \array_map(fn (array $v) => new Nameserver(...$v), $nameservers);
$nameserver = new Nameserver(...$nameserver);
}
$DohConfig = new DoH\DoHConfig($nameservers); $DohConfig = new DoH\DoHConfig($nameservers);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$result = Dns\resolve($hostname); $result = Dns\resolve($hostname);
/** @var Record $record */ /** @var DnsRecord $record */
$record = $result[0]; $record = $result[0];
$inAddr = @\inet_pton($record->getValue()); $inAddr = @\inet_pton($record->getValue());
$this->assertNotFalse( $this->assertNotFalse(
@ -38,42 +38,40 @@ class IntegrationTest extends AsyncTestCase
} }
/** /**
* @param non-empty-list<list{0: string, 1?: NameserverType}> $nameservers
* @group internet * @group internet
* @dataProvider provideServers * @dataProvider provideServers
*/ */
public function testWorksAfterConfigReload($nameservers) public function testWorksAfterConfigReload($nameservers): void
{ {
foreach ($nameservers as &$nameserver) { $nameservers = \array_map(fn (array $v) => new Nameserver(...$v), $nameservers);
$nameserver = new Nameserver(...$nameserver);
}
$DohConfig = new DoH\DoHConfig($nameservers); $DohConfig = new DoH\DoHConfig($nameservers);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
Dns\resolve('google.com'); Dns\resolve('google.com');
$this->assertNull(Dns\resolver()->reloadConfig()); /** @psalm-suppress UndefinedInterfaceMethod */
$this->assertNull(dnsResolver()->reloadConfig());
delay(0.5); delay(0.5);
$result = \is_array(Dns\resolve('google.com')); $this->assertIsArray(Dns\resolve('google.com'));
$this->assertTrue($result);
\usleep(500*1000); \usleep(500*1000);
} }
/** /**
* @param non-empty-list<list{0: string, 1?: NameserverType}> $nameservers
* @group internet * @group internet
* @dataProvider provideServers * @dataProvider provideServers
*/ */
public function testResolveIPv4only($nameservers) public function testResolveIPv4only($nameservers): void
{ {
foreach ($nameservers as &$nameserver) { $nameservers = \array_map(fn (array $v) => new Nameserver(...$v), $nameservers);
$nameserver = new Nameserver(...$nameserver);
}
$DohConfig = new DoH\DoHConfig($nameservers); $DohConfig = new DoH\DoHConfig($nameservers);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$records = Dns\resolve("google.com", Record::A); $records = Dns\resolve("google.com", DnsRecord::A);
/** @var Record $record */ /** @var DnsRecord $record */
foreach ($records as $record) { foreach ($records as $record) {
$this->assertSame(Record::A, $record->getType()); $this->assertSame(DnsRecord::A, $record->getType());
$inAddr = @\inet_pton($record->getValue()); $inAddr = @\inet_pton($record->getValue());
$this->assertNotFalse( $this->assertNotFalse(
$inAddr, $inAddr,
@ -84,22 +82,21 @@ class IntegrationTest extends AsyncTestCase
} }
/** /**
* @param non-empty-list<list{0: string, 1?: NameserverType}> $nameservers
* @group internet * @group internet
* @dataProvider provideServers * @dataProvider provideServers
*/ */
public function testResolveIPv6only($nameservers) public function testResolveIPv6only($nameservers): void
{ {
foreach ($nameservers as &$nameserver) { $nameservers = \array_map(fn (array $v) => new Nameserver(...$v), $nameservers);
$nameserver = new Nameserver(...$nameserver);
}
$DohConfig = new DoH\DoHConfig($nameservers); $DohConfig = new DoH\DoHConfig($nameservers);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$records = Dns\resolve("google.com", Record::AAAA); $records = Dns\resolve("google.com", DnsRecord::AAAA);
/** @var Record $record */ /** @var DnsRecord $record */
foreach ($records as $record) { foreach ($records as $record) {
$this->assertSame(Record::AAAA, $record->getType()); $this->assertSame(DnsRecord::AAAA, $record->getType());
$inAddr = @\inet_pton($record->getValue()); $inAddr = @\inet_pton($record->getValue());
$this->assertNotFalse( $this->assertNotFalse(
$inAddr, $inAddr,
@ -110,41 +107,41 @@ class IntegrationTest extends AsyncTestCase
} }
/** /**
* @param non-empty-list<list{0: string, 1?: NameserverType}> $nameservers
* @group internet * @group internet
* @dataProvider provideServers * @dataProvider provideServers
*/ */
public function testPtrLookup($nameservers) public function testPtrLookup($nameservers): void
{ {
foreach ($nameservers as &$nameserver) { $nameservers = \array_map(fn (array $v) => new Nameserver(...$v), $nameservers);
$nameserver = new Nameserver(...$nameserver);
}
$DohConfig = new DoH\DoHConfig($nameservers); $DohConfig = new DoH\DoHConfig($nameservers);
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig)); dnsResolver(new DoH\Rfc8484StubDohResolver($DohConfig));
$result = Dns\query("8.8.4.4", Record::PTR); $result = Dns\query("8.8.4.4", DnsRecord::PTR);
/** @var Record $record */ /** @var DnsRecord $record */
$record = $result[0]; $record = $result[0];
$this->assertSame("dns.google", $record->getValue()); $this->assertSame("dns.google", $record->getValue());
$this->assertNotNull($record->getTtl()); $this->assertNotNull($record->getTtl());
$this->assertSame(Record::PTR, $record->getType()); $this->assertSame(DnsRecord::PTR, $record->getType());
\usleep(500*1000); \usleep(500*1000);
} }
/**
* @return iterable<list{string, list<list{0: string, 1?: NameserverType}>}>
*/
public function provideServersAndHostnames() public function provideServersAndHostnames()
{ {
$hostnames = $this->provideHostnames(); foreach ($this->provideHostnames() as [$hostname]) {
$servers = $this->provideServers(); foreach ($this->provideServers() as [$nameserver]) {
$result = []; yield [$hostname, $nameserver];
foreach ($hostnames as $args) {
$hostname = $args[0];
foreach ($servers as $args) {
$nameserver = $args[0];
$result[] = [$hostname, $nameserver];
} }
} }
return $result;
} }
/**
* @return list<list{string}>
*/
public function provideHostnames() public function provideHostnames()
{ {
return [ return [
@ -160,6 +157,9 @@ class IntegrationTest extends AsyncTestCase
]; ];
} }
/**
* @return iterable<int, list{list<list{0: string, 1?: NameserverType}>}>
*/
public function provideServers() public function provideServers()
{ {
$nameservers = [ $nameservers = [
@ -169,7 +169,6 @@ class IntegrationTest extends AsyncTestCase
['https://mozilla.cloudflare-dns.com/dns-query', NameserverType::GOOGLE_JSON], ['https://mozilla.cloudflare-dns.com/dns-query', NameserverType::GOOGLE_JSON],
['https://dns.google/resolve', NameserverType::GOOGLE_JSON], ['https://dns.google/resolve', NameserverType::GOOGLE_JSON],
]; ];
$result = [];
for ($start = 0; $start < \count($nameservers); $start++) { for ($start = 0; $start < \count($nameservers); $start++) {
$temp = []; $temp = [];
for ($i = 0; $i < \count($nameservers); $i++) { for ($i = 0; $i < \count($nameservers); $i++) {
@ -177,9 +176,7 @@ class IntegrationTest extends AsyncTestCase
$temp[] = $nameservers[$i]; $temp[] = $nameservers[$i];
} }
$result[] = [$temp]; yield [$temp];
} }
return $result;
} }
} }

View File

@ -2,22 +2,25 @@
namespace Amp\DoH\Test; namespace Amp\DoH\Test;
use Amp\Dns\ConfigException; use Amp\Dns\DnsConfigException;
use Amp\DoH\Nameserver; use Amp\DoH\Nameserver;
use Amp\DoH\NameserverType; use Amp\DoH\NameserverType;
use Amp\PHPUnit\AsyncTestCase; use Amp\PHPUnit\AsyncTestCase;
/** @psalm-suppress PropertyNotSetInConstructor */
class NameserverTest extends AsyncTestCase class NameserverTest extends AsyncTestCase
{ {
/** /**
*
* @dataProvider provideValidServers * @dataProvider provideValidServers
*/ */
public function testAcceptsValidServers($nameserver, $type = NameserverType::RFC8484_POST, $headers = []) public function testAcceptsValidServers(string $nameserver, NameserverType $type = NameserverType::RFC8484_POST, array $headers = []): void
{ {
$this->assertInstanceOf(Nameserver::class, new Nameserver($nameserver, $type, $headers)); $this->assertInstanceOf(Nameserver::class, new Nameserver($nameserver, $type, $headers));
} }
/**
* @return list<list{0: string, 1?: NameserverType::RFC8484_POST}>
*/
public function provideValidServers() public function provideValidServers()
{ {
return [ return [
@ -30,16 +33,18 @@ class NameserverTest extends AsyncTestCase
} }
/** /**
*
* @dataProvider provideInvalidServers * @dataProvider provideInvalidServers
*/ */
public function testRejectsInvalidServers($nameserver, $type = NameserverType::RFC8484_POST, $headers = []) public function testRejectsInvalidServers(string $nameserver, NameserverType $type = NameserverType::RFC8484_POST, array $headers = []): void
{ {
$this->expectException(ConfigException::class); $this->expectException(DnsConfigException::class);
new Nameserver($nameserver, $type, $headers); new Nameserver($nameserver, $type, $headers);
} }
public function provideInvalidServers() /**
* @return list<list{0: string, 1?: NameserverType::RFC8484_POST}>
*/
public function provideInvalidServers(): array
{ {
return [ return [
[''], [''],

View File

@ -3,50 +3,51 @@
namespace Amp\DoH\Test; namespace Amp\DoH\Test;
use Amp\Dns\DnsException; use Amp\Dns\DnsException;
use Amp\Dns\DnsRecord;
use Amp\Dns\InvalidNameException; use Amp\Dns\InvalidNameException;
use Amp\Dns\Record; use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Dns\Rfc1035StubResolver;
use Amp\DoH; use Amp\DoH;
use Amp\DoH\Rfc8484StubResolver; use Amp\DoH\Rfc8484StubDohResolver;
use Amp\PHPUnit\AsyncTestCase; use Amp\PHPUnit\AsyncTestCase;
class Rfc8484StubResolverTest extends AsyncTestCase /** @psalm-suppress PropertyNotSetInConstructor */
class Rfc8484StubDohResolverTest extends AsyncTestCase
{ {
public function getResolver() public function getResolver(): Rfc8484StubDohResolver
{ {
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]); $DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]);
return new Rfc8484StubResolver($DohConfig); return new Rfc8484StubDohResolver($DohConfig);
} }
public function testResolveSecondParameterAcceptedValues() public function testResolveSecondParameterAcceptedValues(): void
{ {
$this->expectException(\Error::class); $this->expectException(\Error::class);
$this->getResolver()->resolve("abc.de", Record::TXT); $this->getResolver()->resolve("abc.de", DnsRecord::TXT);
} }
public function testIpAsArgumentWithIPv4Restriction() public function testIpAsArgumentWithIPv4Restriction(): void
{ {
$this->expectException(DnsException::class); $this->expectException(DnsException::class);
$this->getResolver()->resolve("::1", Record::A); $this->getResolver()->resolve("::1", DnsRecord::A);
} }
public function testIpAsArgumentWithIPv6Restriction() public function testIpAsArgumentWithIPv6Restriction(): void
{ {
$this->expectException(DnsException::class); $this->expectException(DnsException::class);
$this->getResolver()->resolve("127.0.0.1", Record::AAAA); $this->getResolver()->resolve("127.0.0.1", DnsRecord::AAAA);
} }
public function testInvalidName() public function testInvalidName(): void
{ {
$this->expectException(InvalidNameException::class); $this->expectException(InvalidNameException::class);
$this->getResolver()->resolve("go@gle.com", Record::A); $this->getResolver()->resolve("go@gle.com", DnsRecord::A);
} }
public function testValidSubResolver() public function testValidSubResolver(): void
{ {
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')], null, new Rfc1035StubResolver()); $DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')], null, new Rfc1035StubDnsResolver());
$this->assertInstanceOf(Rfc8484StubResolver::class, new Rfc8484StubResolver($DohConfig)); $this->assertInstanceOf(Rfc8484StubDohResolver::class, new Rfc8484StubDohResolver($DohConfig));
} }
public function testInvalidNameserverFallback() public function testInvalidNameserverFallback(): void
{ {
$DohConfig = new DoH\DoHConfig( $DohConfig = new DoH\DoHConfig(
[ [
@ -56,8 +57,8 @@ class Rfc8484StubResolverTest extends AsyncTestCase
new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query'), new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query'),
] ]
); );
$resolver = new Rfc8484StubResolver($DohConfig); $resolver = new Rfc8484StubDohResolver($DohConfig);
$this->assertInstanceOf(Rfc8484StubResolver::class, $resolver); $this->assertInstanceOf(Rfc8484StubDohResolver::class, $resolver);
$resolver->resolve('google.com'); $resolver->resolve('google.com');
} }
} }